1

我有一个带有事件和自定义 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 操作。我究竟做错了什么?

4

4 回答 4

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 线程中做太多事情的症状。

于 2013-10-08T19:21:59.397 回答
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")); 
}
于 2013-10-08T19:22:14.707 回答
0

你得到了违规,因为textBox1它由不同的线程拥有。您必须invoke使用另一个线程上的功能。调用就像询问线程(拥有textBox1):“这里线程,当你有时间时执行这个......”。所以拥有 的线程textBox1将执行功能,而不是从引发事件的线程(或调用,因为事件是回调......)。

我会选择这个解决方案。它比调用文本框本身更通用。并且事件的作用并不重要,因此您不必将事件完成的所有功能都放在 a (Action)orBeginInvoke中。您只需从拥有该文本框的线程再次调用该事件即可。

本主题中的其他答案不检查是否需要调用。我会说,那个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";
    }
于 2013-10-10T08:25:11.403 回答
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"; 
   });
}
于 2013-10-08T19:27:30.140 回答