【问题标题】:GUI Invoke with EventHandlers使用事件处理程序的 GUI 调用
【发布时间】:2013-11-17 04:28:11
【问题描述】:

我有一个类似“ClientSocket.cs”的类

  class ClientSocket {
     public delegate void ConnectHandler(object sender, EventArgs e);
     public event ConnectHandler ConnectEvent = delegate { };

     protected void OnConnectEvent(EventArgs e) {
        ConnectHandler ev = ConnectEvent;
        ev(this, e);
    }

  }

还有另一个类“myForm.cs”

public partial class myForm : Form {
    private ClientSocket client;

    private void button1_Click(object sender, EventArgs e) {
        client = new ClientSocket();
        client.ConnectEvent += myForm_OnConnectEvent;

        client.connect();
    }


    // Handler for ConnectEvent
    private void myForm_OnConnectEvent(object sender, EventArgs e) {
        //this.BeginInvoke((MethodInvoker)delegate { writeLog("Connected"); }); 

        writeLog("Connected");
    }

    // Function that write a log string to a TextBox
    public writeLog(string log) {
        guiTextBox.AppendText(log);
    }
  }

问题来了。 我尝试使用 BeginInvoke 调用 writeLog 或直接调用它。有时我在写入 guiTextBox 时会收到 InvalidOperationException。 我不明白为什么我会收到那条消息。该事件由 ClientSocket 对象触发,但事件处理程序是相对于主 UI 线程 (myForm)。

我可以避免对我班级的每个 EventHandler 使用 BeginInvoke/Invoke 吗?


编辑:我了解有什么区别,现在我尝试了解调用事件的最佳做法。

在 BASE 类中引发事件(在这种情况下为 ClientSocket)时,我是否应该将 BeginInvoke/Invoke 方法放入

    protected void OnConnectEvent(EventArgs e) {
        ConnectHandler ev = ConnectEvent;

        this.BeginInvoke((MethodInvoker)delegate { ev(this, e);});
    }

或者我应该把它当我使用那个对象并向那个处理程序添加一个监听器

    // Handler for ConnectEvent used in GUI (myForm)
    private void myForm_OnConnectEvent(object sender, EventArgs e) {
        this.BeginInvoke((MethodInvoker)delegate { writeLog("Connected"); }); 
    }

干杯

【问题讨论】:

  • 完整的解释见这个 MSDN 帖子,msdn.microsoft.com/en-us/library/ms171728%28v=vs.85%29.aspx
  • MSDN 很清楚这一点。现在我只想知道使用 BeginInvoke 的最佳实践。我应该在 ClientSocket 的 EventHandler 中还是在 myForm 中调用“BeginInvoke”?
  • 引用的示例调用表单的方法来检查 textBox1.InvokeRequired 是否为真,然后继续设置文本。我不完全理解您的问题,因为示例中没有使用BeginInvoke
  • 我了解这个例子,我想了解使用 BeginInvoke/Invoke 的最佳实践是什么。我试图解释我最好的(编辑)

标签: c# user-interface invoke


【解决方案1】:

ClientSocket 中的this.BeginInvoke 不存在。为了能够执行BeginInvoke,必须在具有该方法的对象上调用它(在您的情况下是您的表单)。

如果您希望在 ClientSocket 类中进行调用,则需要传入具有 BeginInvoke 函数的 Control

但是,如果我在哪里写这篇文章,我不会这样做。它向ClientSocket 添加了一个不必要的要求,即您必须传入Control(这称为Tightly Coupling,您应该在编程中尽量避免它)。就我个人而言,我会让事件在它想要引发的任何线程中传递,让消费者担心进行任何特殊调用(如果他们甚至需要的话)。

这是我将如何编写myForm_OnConnectEvent,此模式检查我们是否需要调用,如果我们这样做,它会使用相同的参数再次调用该函数,但这次是在 UI 线程上。

// Handler for ConnectEvent used in GUI (myForm)
private void myForm_OnConnectEvent(object sender, EventArgs e) 
{
    if(this.InvokeRequired)
    {
        this.BeginInvoke(new ConnectHandler(myForm_OnConnectEvent), new object[] {sender, e});
        return;
    }

    writeLog("Connected");
}

附带说明,我不知道 writeLog 正在做什么 (it should have a capital W by the way),但如果它不与 UI 交互,则根本不需要进行任何调用。如果它与 UI 上的 TextBox 或其他内容交互,就是我要调用的地方。

private void myForm_OnConnectEvent(object sender, EventArgs e) 
{
    writeLog("Connected");
}

private void writeLog(string logMessage) 
{
    if(logTextBox.InvokeRequired)
    {
        logTextBox.BeginInvoke(new Action<string>(writeLog), logMessage);
        return;
    }

    var logLine = String.Format("{0:g}: {1}{2}", DateTime.Now, logMessage, Enviorment.NewLine);
    logTextBox.AppendText(logLine);
}

【讨论】:

  • 这么清楚!是的,当然我写了一个不好的例子(使用 this.BeginInvoke 代替 myControl.BeginInvoke [ClientSocket.cs])。我来自 Java 和 Python 语言,所以我习惯使用第一个小写字母 :) 谢谢
【解决方案2】:

事件处理程序是在myForm中声明的,但执行处理程序的线程是由ClientSocket类的逻辑定义的。如果这将是后台线程,则将从后台线程引发事件处理程序,因此,您需要BeginInvoke 以避免跨线程访问控件。

换句话说:任何类型的任何方法的归属都与将执行此方法的线程无关。这些东西(类型和线程)是平行宇宙。

顺便说一句,你可以替换这个:

public delegate void ConnectHandler(object sender, EventArgs e);
public event ConnectHandler ConnectEvent = delegate { };

用这个:

public event EventHandler ConnectEvent;

没有必要再创建一个委托类型了。

【讨论】:

  • 我使用了“委托处理程序”,因为在 WEB 上的许多示例中,人们使用的是“代码样式”。有什么不同?而且,我应该在 ClientSocket 的 EventHandler 中还是在 myForm 中调用“BeginInvoke”?
  • 好答案。但我只能选择一个答案。
猜你喜欢
  • 1970-01-01
  • 2011-01-21
  • 1970-01-01
  • 1970-01-01
  • 2016-04-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多