我有一个带有事件和自定义EventArgs的类。有意义的代码:
public void OnTickReceived(TickReceivedEventArgs e)
{
EventHandler<TickReceivedEventArgs> handler = TickReceived;
if (handler != null)
handler(this, e);
}
public event EventHandler<TickReceivedEventArgs> TickReceived = delegate { };
并在UI Windows窗体中使用这个类来订阅像这样的事件
private void button4_Click(object sender, EventArgs e)
{
bool esito;
t = new T3OpenStockReader();
esito = t.Connect();
textBox1.Text += "Connection: " + esito.ToString() + "\r\n";
Application.DoEvents();
if (esito)
{
esito = t.Subscribe("MI.EQCON.2552");
textBox1.Text += "Subscription: " + esito.ToString() + "\r\n";
Application.DoEvents();
}
if (esito)
{
t.Start();
t.TickReceived += NewTick_Event;
System.Diagnostics.Debug.Print("Reading started...");
}
}
private void NewTick_Event(object sender, TickReceivedEventArgs e)
{
textBox1.Text += e.tick.DT + " " + e.tick.Price + " " + e.tick.Volume + "\r\n";
}
我收到InvalidOperationException
- cross.thread操作。我做错了什么?
答案 0 :(得分:3)
我收到InvalidOperationException - cross.thread操作。我的错误在哪里?
大概T3OpenStockReader
会在自己的线程上引发事件 - 但是你试图修改事件处理程序中的UI ...这意味着你在错误的线程中执行它。您应该将事件处理程序更改为:
private void NewTick_Event(object sender, TickReceivedEventArgs e)
{
Action action = () => textBox1.Text += e.tick.DT + " " + e.tick.Price
+ " " + e.tick.Volume + "\r\n";
textBox1.BeginInvoke(action);
}
我还建议您摆脱Application.DoEvents()
次呼叫 - 它们是在UI线程中尝试做太多的一种症状。
答案 1 :(得分:1)
我假设您正在尝试更新非UI线程上的UI组件,即NewTick_Event
。您需要强制更新回到UI线程,例如
private void NewTick_Event(object sender, TickReceivedEventArgs e)
{
textBox1.Invoke(new Action(() => textBox1.Text += e.tick.DT + " " + e.tick.Price + " " + e.tick.Volume + "\r\n"));
}
答案 2 :(得分:0)
从另一个线程调用NewTick_Event,并且必须在UI线程上调用控件上的更改(例如,通过使用BeginInvoke方法)
private void NewTick_Event(object sender, TickReceivedEventArgs e)
{
this.BeginInvoke( (Action) () =>
{
textBox1.Text += e.tick.DT + " " + e.tick.Price + " " + e.tick.Volume + "\r\n";
});
}
答案 3 :(得分:0)
您收到违规行为,因为textBox1
归其他线程所有。您必须invoke
其他线程上的功能。调用就像询问线程(那些拥有textBox1
):“这里的线程,当你有时间时执行它......”。所以拥有textBox1
的线程将执行该功能,而不是从该事件引发的线程(或调用,因为事件是回调...)。
我会选择这个解决方案。它比调用文本框本身更通用。事件的作用并不重要,因此您不必将事件完成的所有功能都放在(Action)
或BeginInvoke
中。您只需从拥有文本框的线程中再次调用该事件。
本主题中的其他答案不检查是否需要调用。我会说,InvokeRequired
布尔是有原因的。
以下示例中的FormParentOfTextBox
是文本框所在表单的实例。你也可以在这里使用textBox1
,但同样,它将变得不那么通用。
private void NewTick_Event(object sender, TickReceivedEventArgs e)
{
//Thread safe approach, generally for every event.
if (FormParentOfTextBox.InvokeRequired)
{
this.Invoke(NewTick_Event(sender, e));
return;
}
textBox1.Text += e.tick.DT + " " + e.tick.Price + " " + e.tick.Volume + "\r\n";
}