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
版权:本文版权归作者和博客园共有
转载:欢迎转载,但未经作者同意,必须保留此段声明;必须在文章中给出原文连接;否则必究法律责任

posted @   兮去  阅读(2926)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 上周热点回顾(3.3-3.9)
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
点击右上角即可分享
微信分享提示