C# Winforms Control.BeginInvoke 和 Action<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);
}
});
}
方法三:使用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)并在您的项目中重用。
为什么方法一不能正确执行,通过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);
}
});
}
方法三:使用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)并在您的项目中重用。