簡體   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