web-dev-qa-db-ja.com

イベントハンドラからメインスレッドでメソッドを実行します

Queueクラスから継承されたカスタムQueueクラスがあります。イベントItemAddedがあります。このイベントのイベントハンドラーで、メソッドを実行しています。しかし、メインスレッドで実行したいのですが、メインスレッド以外で実行されています。やり方がわかりません。なにか提案を ?

//My custom class


using System;
using System.Collections; //Required to inherit non-generic Queue class.

namespace QueueWithEvent
{
    public class SmartQueue:Queue
    {

        public delegate void ItemAddedEventHandler(object sender, EventArgs e);

        public event ItemAddedEventHandler ItemAdded;

        protected virtual void OnItemAdded(EventArgs e)
        {
           if (ItemAdded != null)
           {
              ItemAdded(this, e);
           }
    }

    public override void Enqueue(object Item)
    {
        base.Enqueue(Item);
        OnItemAdded(EventArgs.Empty);
    }        

   }
}



 //Winform application

 using System;
 using System.ComponentModel;
 using System.Windows.Forms;
 using QueueWithEvent;

 namespace TestApp
 {
    public partial class Form1 : Form
    {

    SmartQueue qTest = new SmartQueue();

    public Form1()
    {
        InitializeComponent();
        qTest.ItemAdded += new SmartQueue.ItemAddedEventHandler(this.QChanged);
    }

    private void btnStartBgw_Click(object sender, EventArgs e)
    {
        DisplayThreadName();
        bgwTest.RunWorkerAsync();
    }

    private void bgwTest_DoWork(object sender, DoWorkEventArgs e)
    {
        try
        {
            for (int i = 0; i < 11; i++)
            {
                string valueTExt = i.ToString();
                qTest.Enqueue(valueTExt);
                System.Threading.Thread.Sleep(5000);
            }
        }
        catch (Exception Ex)
        {
            MessageBox.Show(Ex.Message);

        }
    }


    private void DisplayThreadName()
    {
        string tName = System.Threading.Thread.CurrentThread.ManagedThreadId.ToString();
        txtThreadName.Text = tName;           
    }

    private void QChanged(object sender, EventArgs e)
    {
        //#########I want this method to run on main thread #############
        DisplayThreadName();
    }
}
}

前もって感謝します。 SKPaul。

10
s.k.paul

バックグラウンドスレッドでアイテムをキューに入れているため(DoWorkイベントハンドラーはバックグラウンドスレッドで実行されます)、イベントはバックグラウンドスレッドでも発生します。

InvokeRequiredメソッドを使用して、UIスレッドを使用しているかどうかを確認します。そうでない場合は、Invokeを使用してUIスレッドでコードを実行します。

 private void QChanged(object sender, EventArgs e)
 {
     if (InvokeRequired)
     {
         Invoke((MethodInvoker)delegate { QChanged(sender, e); });
         return;
     }
     // this code will run on main (UI) thread 
     DisplayThreadName();
 }

別のオプション-ProgressChangedイベントを使用してアイテムをキューに入れます(WorkerReportsProgressをtrueに設定することを忘れないでください)。このイベントハンドラーはUIスレッドで実行されます。

private void bgwTest_DoWork(object sender, DoWorkEventArgs e)
{
    BackgroundWorker worker = (BackgroundWorker)sender;

    for (int i = 0; i < 11; i++)
    { 
        // use user state for passing data
        // which is not reflecting progress percentage
        worker.ReportProgress(0, i);
        System.Threading.Thread.Sleep(5000);
    }
}

private void bgwTest_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
     string valueTExt = e.UserState.ToString();
     qTest.Enqueue(valueTExt);
}
22

BackgroundWorkerと同じアプローチを使用できます。つまり、AsyncOperationを次のように組み込んでいます。クラス内のメンバー。作成されたスレッドに操作をディスパッチできます。

protected AsyncOperation AsyncOp;

「null」引数を使用してコンストラクターでインスタンス化します。これにより、現在のスレッドに「バインド」された非同期操作が作成されます。

public SmartQueue()
{
    AsyncOp = AsyncOperationManager.CreateOperation(null);
}

次に、AsyncOpを使用してイベントをPostできます。

public override void Enqueue(object Item)
{
    base.Enqueue(Item);
    AsyncOp.Post(OnItemAdded, EventArgs.Empty);
}

これにより、SmartQueueインスタンスを作成したのと同じスレッドでOnItemAddedハンドラー(サブスクライバー)が実行されます。

1
Honza Brestan