dポイントプレゼントキャンペーン実施中!

親クラスウィンドウのボタンを押すと子クラスを作成して実行し、
子クラスは処理の経過や結果を親クラスに通知する、
というような処理をするために、下のようなコードを書きました。
(主要な部分のみの抜粋です。)

子クラスでは、親クラスへの通知後も引き続いて処理できるよう、
親クラスに対して非同期で通知したいため、
BeginInvoke を使用することにしました。

実行すると、「★A★」で親クラスに通知を実行した際、
・「★B★」は本来モーダルのはずが裏に回ってしまう
・「★C★」ではクロススレッド呼び出し方法の例外が発生
という問題が生じます。

BeginInvokeするとUIスレッドとは別のスレッドとしてChildEnventReceivedが呼ばれるためと解釈しています。(ほんとかな。)

こういったことをしたい場合、どうするのが普通でしょう?

下のコード自体はだいたい合っていて、
コントロールの操作は例えば「★C★」ではbtn.Invoke()するのが普通でしょうか?
とすると、そのためのdelegateやメッソドを定義しなければならず、
煩雑になりますね。
C++のPostMessageのようなことがやりたいだけなのですが。

C#は最近取り組み始めたばかりで、勉強中です。
普通はこうする、というような処理をご教授いただければ幸いです。

よろしくお願いします。

================================================
// 子クラスからの通知イベント
delegate void EventHandler();

public Parent
{
  EventHandler handler;

  private void btn_Click(object sender, EventArgs e)
  {
    btn.Enabled = false;

    handler = new EventHandler(ChildEventReceived);
    Child child = new Child();
    child.SendEvent += hander;
    child.Exec();
  }

  void ChildEnventReceived();
    MessageBox.Show("Child State Received");
    ★B★このメッセージボックスが裏に隠れてしまう★B★

    btn.Enabled = true;
    ★C★この操作は「有効でないスレッド間の操作」となる★C★
  }
}

class Child
{
  public event EventHandler SendEvent;

  public void Exec()
  {
    ★A★ここで親に対して通知する★A★
    SendEvent.BeginInvoke(null, null);
  }
}
================================================

A 回答 (1件)

using System;


using System.Threading;
using System.Windows.Forms;

delegate void EventHandler();

public class Parent:Form{

EventHandler handler;

Button btn;
public Parent(){
btn=new Button();
btn.Text="Exec";
btn.Click+=this.btn_Click;
Controls.Add(btn);
}

private void btn_Click(object sender, EventArgs e){
btn.Enabled = false;

// ★そもそも処理は UI スレッドではなく別スレッドでするのでは?★
// UIスレッドで処理をすると終わるまで Form が固まります。
// Form のメッセージを適宜処理する為に Application.DoEvents を Exec 内で定期的に呼び出す事もできますが、
// 同期的な実行になるので今回の非同期的に実行するという目的に適いません。
new Thread(new ThreadStart(()=>{
handler = new EventHandler(ChildEventReceived);
Child child = new Child();
child.SendEvent += handler;
child.Exec();
})).Start();
}

void ChildEventReceived(){
// ★Form.BeginInvoke を使えば、その Form の UI スレッド上で非同期的に実行される。★
this.BeginInvoke(new Action(()=>{
MessageBox.Show("Child State Received");

btn.Enabled = true;
}));
}

public static void Main(){
Application.Run(new Parent());
}
}

class Child{
public event EventHandler SendEvent;

public void Exec(){
Thread.Sleep(1000); // 重い処理など

// ★イベント自体の発火は同期で良いかと思います★
// 同期的に実行するかどうかは handler に任せた方が自然だと思います
if(SendEvent!=null)SendEvent();
}
}
    • good
    • 0

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!