繁体   English   中英

C# Winforms Control.BeginInvoke with Action<t></t>

[英]C# Winforms Control.BeginInvoke with Action<T>

为什么方法一不能正确执行,通过Action<'string'>updateMsgAction,给ListBox添加一条消息,方法二却能正确执行?

我尝试使用方法 3 BeginInvoke(Delegate, Object[]) ,编译器显示错误。

我希望方法 #2 可以像方法 #1 一样成为需要更新消息时调用的方法。

方法 1 :使用UpdateMsg Action<string>更新消息

    private Action<string> updateMsgAction;

    public void UpdateMsg()
    {
        updateMsgAction = new Action<string>( (s) =>
        {
            MsgList.Items.Add(s);

            if (MsgList.Items.Count > 1000)
            {
                MsgList.Items.RemoveAt(0);
            }
        });
    }

方法 3 :使用BeginInvoke(Delegate, Object[])

    public delegate void MyDelegate(ListBox myControl, string myMessage);

    public void DelegateMethod(ListBox myControl, string myMsg)
    {
        myControl.Items.Add(myMsg);
    }

MQTT 服务器启动:

    public async void StartMqttServer()
    {
        try
        {
            var mqttFactory = new MqttFactory();

            if (mqttServer == null)
            {
                var mqttServerOptions = new MqttServerOptionsBuilder().WithDefaultEndpoint().WithDefaultEndpointPort(int.Parse(txtBoxPort.Text)).Build();
                mqttServer.ClientConnectedHandler = new MqttServerClientConnectedHandlerDelegate(OnMqttServerClientConnected);
                mqttServer.ClientDisconnectedHandler = new MqttServerClientDisconnectedHandlerDelegate(OnMqttServerClientDisconnected);
                mqttServer.ClientSubscribedTopicHandler = new MqttServerClientSubscribedTopicHandlerDelegate(OnMqttServerCleitnSubScribedTopic);
                mqttServer.ClientUnsubscribedTopicHandler = new MqttServerClientUnsubscribedTopicHandlerDelegate(OnMqttServerCleitnUnsubScribedTopic);
                mqttServer.ApplicationMessageReceivedHandler = new MqttApplicationMessageReceivedHandlerDelegate(OnMqttServerApplicationMessageReceived);
                await mqttServer.StartAsync(mqttServerOptions);

                MsgList.BeginInvoke(updateMsgAction, "MQTT Server is started."); //Method 1

                /*MsgList.BeginInvoke(new Action<string>((s) =>
                { MsgList.Items.Add(s); }), "MQTT Server is started."));*/ //Method 2

                //MsgList.BeginInvoke(new MyDelegate(DelegateMethod), "MQTT Server is started.")); //Method 3
            }
        }
        catch(Exception ex)
        {
            MsgList.BeginInvoke(updateMsgAction, "MQTT Server start fail."));
        }
    }

OnMqttServerApplicationMessageReceived:

    public void OnMqttServerApplicationMessageReceived(MqttApplicationMessageReceivedEventArgs e)
    {
     // Method 1
     MsgList.BeginInvoke(updateMsgAction, 
       String.Format("Client[{0}]>> Topic:{1} Payload:{2} Qos:{3} Retain:{4}",
          e.ClientId, e.ApplicationMessage.Topic, e.ApplicationMessage.Payload.ToString(),
          e.ApplicationMessage.QualityOfServiceLevel, e.ApplicationMessage.Retain));
    }

BeginInvoke 用于在创建控件的线程中执行一些代码。 它是他们自己的线程(通常在主线程中)中的强制更新控件,否则您会遇到异常。 因此,您的使用或 BeginInvoke 是正确的。

问题是,当您在主线程中运行并且能够更新控件时,您将更新委托给一个操作。 该操作在其他线程中运行,您正在“取消”de BeginInvoke 并获得预期的异常以尝试更新其他线程中的控件。

我将 SynchronizationContext 用于此类事情。 在您的表单代码中,添加一个变量:

private static SynchronizationContext Context;

更新:并在构造函数中初始化:

    public YourForm()
    {
        this.InitializeComponent();

        Context = SynchronizationContext.Current;

        // Other code
    }

添加此方法:

private static void RunInMainThread(Action operation)
    {
        if (Context != SynchronizationContext.Current)
        {
            Context.Post(o => operation(), null);
        }
        else
        {
            operation();
        }
    }

如果您已经在主线程中运行,您的代码会立即运行。 在其他情况下,Post 动作在主线程中异步运行。 您可以改用 Send 或 Post 来同步运行。 并在需要访问控件时使用它:

public void OnMqttServerApplicationMessageReceived(MqttApplicationMessageReceivedEventArgs e)
{    
   var msg = string.Format(
       "Client[{0}]>> Topic:{1} Payload:{2} Qos:{3} Retain:{4}",
       e.ClientId, 
       e.ApplicationMessage.Topic, 
       e.ApplicationMessage.Payload.ToString(),
       e.ApplicationMessage.QualityOfServiceLevel, 
       e.ApplicationMessage.Retain);

    RunInMainThread(() => 
    {
        MsgList.Items.Add(msg);
        // Other code...
    }
}

您可以为 SynchronizationContext 而不是 RunInMainThread 创建扩展方法(Post 和 Send)并在您的项目中重用。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM