[英]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.