c# - 使用事件处理程序进行 GUI 调用

标签 c# user-interface invoke

我有一个类似“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 类中引发事件时,我是否应该放置 BeginInvoke/Invoke 方法(在这种情况下为 ClientSocket)

    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"); }); 
    }

干杯

最佳答案

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 交互,则根本不需要执行任何调用。如果它与 TextBox 或 UI 上的其他内容交互,就是我进行调用的地方。

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);
}

关于c# - 使用事件处理程序进行 GUI 调用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19916251/

相关文章:

c# - RabbitMQ 可以告诉我一条消息已传递了多少次吗?

c# - 将 OrderBy Predicate 中的 Lambda 表达式从 C# 转换为 VB.net

c# - 非托管代码中 System.Diagnostics.Debugger.Launch() 的等效项是什么?

c# - MVC 5 bundles.ignoreList.Clear 不起作用

java - Java 中的 GUI 控制流和回调

c# 将 console.writeline 转换为字符串

java - 调用 servlet 类中的方法

java - 如何每秒刷新一次 View 以实时反射(reflect)变化?

ios - 为 CarPlay 音频应用程序添加 UI

c# - 两次调用 Dispatcher.Invoke() 时出现死锁