https://www.codenong.com/cs106719464/
文章来源地址https://www.toymoban.com/news/detail-711873.html
文章来源:https://www.toymoban.com/news/detail-711873.html
WinForm中的UI假死其实是个老生常谈的问题了,但最近还是很多人问我该如何解决,所以今天就来说明一下如何解决UI假死的问题。实验程序界面如下图所示:
方法一:async + await + Task
首先看下面一段代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
using System;
using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Windows.Forms; namespace WindowsFormsApplication1 { public partial class Form1 : Form { public Form1() { InitializeComponent(); } // 开始 private void btnStart_Click(object sender, EventArgs e) { string message = GetMessage(); MessageBox.Show(message); } // 一个耗时任务 private string GetMessage() { Thread.Sleep(10000); return "Hello World"; } } } |
在上面的代码中,GetMessage()方法耗时10秒钟,如果你点击按钮,那么在10秒钟内窗体将处于假死状态。这种情况很常见,之所以会造成UI假死的原因也很简单:某个函数耗时太久。在遇见这种情况的时候,我们就可以考虑使用async + await + Task来解决,代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
using System;
using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Windows.Forms; namespace WindowsFormsApplication1 { public partial class Form1 : Form { public Form1() { InitializeComponent(); } // 开始 private async void btnStart_Click(object sender, EventArgs e) { string message = await GetMessage(); MessageBox.Show(message); } // 一个耗时任务 private async Task<string> GetMessage() { return await Task<string>.Run(() => { Thread.Sleep(10000); return "Hello World"; }); } } } |
运行之后点击按钮,你会发现UI没有假死,窗体可以随意拖动了。
方法二:使用BackgroundWorker组件
在很多时候,我们需要动态显示当前的程序执行进度,以便让用户了解程序已经执行到哪一步了。很多同志都会这么写:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
using System;
using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Windows.Forms; namespace WindowsFormsApplication1 { public partial class Form1 : Form { public Form1() { InitializeComponent(); } // 开始 private void btnStart_Click(object sender, EventArgs e) { int max = pgbStatus.Maximum; for (int i = 1; i <= max; i++) { pgbStatus.Value++; Thread.Sleep(1000); } } } } |
功能确实是实现了,进度条能够显示当前执行的进度,可惜UI还是处于假死状态,所以用户体验还是不好。其实WinForm已经给我们提供了一个处理多线程任务的组件BackgroundWorker,使用它可以轻松让你的程序告别UI假死,如下图所示:
代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
using System;
using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Windows.Forms; namespace WindowsFormsApplication1 { public partial class Form1 : Form { public Form1() { InitializeComponent(); this.bgw.WorkerReportsProgress = true; this.bgw.WorkerSupportsCancellation = true; this.bgw.DoWork += DoWork; this.bgw.ProgressChanged += ProgressChanged; this.bgw.RunWorkerCompleted += RunWorkerCompleted; } // 开始 private void btnStart_Click(object sender, EventArgs e) { if (bgw.IsBusy) { return; } bgw.RunWorkerAsync(); } // DoWork private void DoWork(object sender, DoWorkEventArgs e) { int max = pgbStatus.Maximum; for (int i = 1; i <= max; i++) { bgw.ReportProgress(i); Thread.Sleep(1000); } } // ProgressChanged private void ProgressChanged(object sender, ProgressChangedEventArgs e) { pgbStatus.Value = e.ProgressPercentage; } // RunWorkerCompleted private void RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { MessageBox.Show("完成"); } } } |
运行程序点击按钮,你会发现UI没有处于假死状态,窗体可以随意拖动。
方法三:Task + 委托(回调函数)
首先需要明确一点:UI线程位于主线程,如果想要在子线程里更新UI状态,必须要将其切换到主线程,最后进行更新操作。UI控件一般会提供Invoke、InvokeRequired,其中InvokeRequired用于判断是否有子线程在更新UI控件,如果有则返回true,Invoke用于将控制权切换到UI线程,代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
using System;
using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Windows.Forms; namespace WindowsFormsApplication1 { public partial class Form1 : Form { public Form1() { InitializeComponent(); } // 开始 private void btnStart_Click(object sender, EventArgs e) { Task task = Task.Run(() => { int max = pgbStatus.Maximum; for (int i = 1; i <= max; i++) { UpdateValue(i); Thread.Sleep(1000); } }); } // 处理线程 private void UpdateValue(int num) { if (pgbStatus.InvokeRequired) { pgbStatus.Invoke(new Action<int>(UpdateValue), new object[] { num }); } else { pgbStatus.Value = num; } } } } |
方法三也可以解决UI的假死问题,当然也不一定要用Task,利用Thread也可以实现一样的效果。
到了这里,关于WinForm中UI假死的解决方法的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!