C#使用Task在Winform建立控件上的提示等待窗口,实现局部等待加载,不影响主线程(一)
需求:在开发过程中经常需要部分数据等待的加载。可能因为某个表比较慢而卡到主线程,所以需要实现局部加载等待。(在Web肯定就ajax异步就好做,winform就比较复杂点了)
C#使用Task在Winform建立控件上的提示等待窗口,实现局部等待加载,不影响主线程(二)
效果图
效果图的主要效果没展示出来,就这样吧。懒了懒了!
用用户控件新建等待窗口
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 60 61 62 | <br> //Loding2的类 public partial class Loading2 : UserControl, IDisposable //继承IDisposable { Timer timer1 = new Timer(); public Loading2(Control contorl) { InitializeComponent(); this .Parent = contorl; this .Parent.Enabled = false ; this .Visible = false ; //默认不显示<br>timer1=new System.Windows.Forms.Timer(this.components);<br>timer1.Interval=30000;<br>timer1.Tick+=new System.EventHandler(this.timer1_Tick); } public void Close() { //自写Close方法释放资源 this .Dispose(); }<br> //开始运行计时以及显示 public void Start() { this .SendToBack(); this .Visible = true ; //显示 timer1.Enabled = true ; //开始计时 } private void Loading2_Layout( object sender, LayoutEventArgs e) { ReLocation(); //绘制位置到父级控件中间 } private void ReLocation() { int x = ( int )(0.5 * (Parent.Width - this .Width)); int y = ( int )(0.5 * (Parent.Height - this .Height)); this .Location = new System.Drawing.Point(x, y); } private void timer1_Tick( object sender, EventArgs e) { LogHelper.Instance.Error( "加载超时!" );<br> timer1.Stop(); //手动停止,不然就算用户控件Dispose了,timer也不会释放和停止 Close(); //超时关闭 } /// <summary> /// 使用new关键字覆盖原有System.Windows.Forms的Dispose /// </summary> private new void Dispose() { if ( this .InvokeRequired) //在线程中调用,使用Invoke执行是否该用户控件代码 { this .Invoke( new Action(() => { if ( this .Parent != null ) { this .Parent.Enabled = true ; //启用父控件 } Dispose( true );timer1.Dispose(); //timer1是非托管资源需手动结束回收,避免任务过多导致内存问题程序奔溃 })); } else { if ( this .Parent != null ) { this .Parent.Enabled = true ; } Dispose( true );timer1.Dispose(); } } } |
调用代码
Task<Loading2> task = null; CancellationTokenSource cancelTokenSource = null; private void LoadList() { //当上一个任务还在执行时候,最新任务到来。则取消上个任务 if (task != null) { cancelTokenSource.Cancel(); task = null; } cancelTokenSource = new CancellationTokenSource(); Loading2 w = new Loading2(uiGroupBox1); //这里设置了加载的Loading父级是uigroupbox w.Start(); task = Task.Factory.StartNew(() => { LoadListData();//处理数据,里面对控件的操作需异步处理 return w; }, cancelTokenSource.Token); //ContinueWith添加延续任务 task.ContinueWith(task => { task.Result.Close();//无论异常/取消/完成 都执行关闭 if (task.IsCanceled) { //取消后执行的代码 } if (task.IsCompleted) { //完成后执行的代码 } if (task.IsFaulted) { //报错执行的代码 } },TaskContinuationOptions.AttachedToParent);//,TaskContinuationOptions标明后续任务创建方式或者和task的关系。AttachedToParent表示当前延续任务为task的子任务 }
从调用方式可以看出仅仅只是父级和数据处理方法不同。进行优化
public class ERPLoading { static Task<Loading2> task = null; static CancellationTokenSource cancelTokenSource = null; public static void Show(System.Windows.Forms.Control parentContorl, Action load) {//将变化的内容作为参数传递 //当上一个任务还在执行时候,最新任务到来。则取消上个任务 if (task != null) { cancelTokenSource.Cancel(); task = null; } cancelTokenSource = new CancellationTokenSource(); Loading2 w = new Loading2(parentContorl); w.Start(); task = Task.Factory.StartNew(() => { load.Invoke();//处理数据,里面对控件的操作需异步处理 return w; }, cancelTokenSource.Token); task.ContinueWith(task => { task.Result.Close();//无论异常/取消/完成 都执行关闭 }, TaskContinuationOptions.AttachedToParent); } }
最终调用
ERPLoading.Show(dgv, new Action(()=>LoadListData(where)));
源码在后面一篇
作者:兮去博客
出处:https://www.cnblogs.com/bklsj/p/16784749.html
版权:本文版权归作者和博客园共有
转载:欢迎转载,但未经作者同意,必须保留此段声明;必须在文章中给出原文连接;否则必究法律责任
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 上周热点回顾(3.3-3.9)
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」