Выполнить метод в основном потоке из обработчика события
У меня есть собственный класс 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.
Ответы
Ответ 1
Вы помещаете элементы в фоновый поток (обработчик событий DoWork работает в фоновом потоке), поэтому ваше событие также поднимается в фоновом потоке.
Используйте метод InvokeRequired
, чтобы проверить, находитесь ли вы в потоке пользовательского интерфейса. А если нет, то используйте Invoke
для запуска кода в потоке пользовательского интерфейса:
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). Этот обработчик событий работает в потоке пользовательского интерфейса:
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);
}
Ответ 2
Вы можете использовать тот же подход, что и 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);
}
Это будет выполнять обработчики OnItemAdded
(подписчиков) в том же потоке, который создал экземпляр SmartQueue.
Ответ 3
Попробуйте этот код в основном потоке:
SmartQueue smartQueue = new SmartQueue();
public Form1()
{
InitializeComponent();
smartQueue.ItemAdded += new SmartQueue.ItemAddedEventHandler(smartQueue_ItemAdded);
}
void smartQueue_ItemAdded(object sender, EventArgs e)
{
// add your code in here
}