使用asyc await从事件更新UI

我试图了解如何在使用async / await模式时从事件更新UI。 下面是我在WinForm应用程序上使用的测试代码。 我甚至不确定这是正确的方法。 允许pwe_StatusUpdate方法更新UI有什么必要? 抛出跨线程操作错误。

谢谢阅读。

// calling code ProcessWithEvents pwe = new ProcessWithEvents(); pwe.StatusUpdate += pwe_StatusUpdate; await pwe.Run(); void pwe_StatusUpdate(string updateMsg) { // Error Here: Cross-thread operation not valid: Control '_listBox_Output' accessed from a thread other than the thread it was created on. _listBox_Output.Items.Add(updateMsg); } 

 // Class with long running process and event public delegate void StatusUpdateHandler(string updateMsg); public class ProcessWithEvents { public event StatusUpdateHandler StatusUpdate; public async Task Run() { await Task.Run(() => { for (int i = 0; i < 10; i++) { RaiseUpdateEvent(String.Format("Update {0}", i)); Thread.Sleep(500); } }); } private void RaiseUpdateEvent(string msg) { if (StatusUpdate != null) StatusUpdate(msg); } } 

async模式支持进度更新 。

简而言之,您的async方法可以采用IProgress ,并且您的调用代码会传递该接口的实现(通常是Progress )。

 public class ProcessWithUpdates { public async Task Run(IProgress progress) { await Task.Run(() => { for (int i = 0; i < 10; i++) { if (progress != null) progress.Report(String.Format("Update {0}", i)); Thread.Sleep(500); } }); } } // calling code ProcessWithUpdates pwp = new ProcessWithUpdates(); await pwp.Run(new Progress(pwp_StatusUpdate)); 

您应该使用Control Invoke方法。 它在Control的线程中执行一些代码。 您还可以检查InvokeRequired属性以检查是否需要调用Invoke方法(它检查调用方是否在与创建控件的线程不同的线程上)。

简单的例子:

 void SomeAsyncMethod() { // Do some work if (this.InvokeRequired) { this.Invoke((MethodInvoker)(() => { DoUpdateUI(); } )); } else { DoUpdateUI(); } } void DoUpdateUI() { // Your UI update code here } 

在某些情况下,您应该在调用Invoke方法之前检查ControlIsHandleCreated属性。 如果IsHandleCreated返回false,那么您需要等待Control的句柄创建

//只需像这样声明一个委托

 delegate void Add(string msg); 

//然后像这样声明委托方法:

 var add = new Add((msg) => { _listBox_Output.Items.Add(msg); }); 

//现在只需调用委托:

 void pwe_StatusUpdate(string updateMsg) { _listBox_Output.Invoke(add,updateMsg); } 

这是另一个例子

 async void DoExport() { var rMsg = ""; var t = await Task.Factory.StartNew(() => ExportAsMonthReport(LastMonth.Name, LastYear.Name, out rMsg)); if (t) { BeginInvoke((Action)(() => { spinnerMain.Visible = false; menuItemMonth.Enabled = true; MetroMessageBox.Show(this, rMsg, "Export", MessageBoxButtons.OK, MessageBoxIcon.Information, 200); })); } else { BeginInvoke((Action)(() => { spinnerMain.Visible = false; menuItemMonth.Enabled = true; MetroMessageBox.Show(this, rMsg, "Export", MessageBoxButtons.OK, MessageBoxIcon.Error, 200); })); } }