Winform异步执行非UI耗时操作

  在用Winform打开窗口,或是执行某些操作时,总会有一些操作非常耗时。而在执行这些方法时,界面却是处于卡死状态的,这对于用户来说是非常不好的体验。本文就介绍,如何将一些与UI无关的耗时操作放在后台执行,而不影响当前用户的界面。

1.同步执行操作

  同步操作即我们正常的操作,下面将代码给出。新建一个Winform应用程序,应该会有如下文件:
在这里插入图片描述
在Form1.Designer.cs中改成以下代码:

namespace WinFormsApp1
{
    partial class Form1
    {
        /// <summary>
        ///  Required designer variable.
        /// </summary>
        private System.ComponentModel.IContainer components = null;

        /// <summary>
        ///  Clean up any resources being used.
        /// </summary>
        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        #region Windows Form Designer generated code

        /// <summary>
        ///  Required method for Designer support - do not modify
        ///  the contents of this method with the code editor.
        /// </summary>
        private void InitializeComponent()
        {
            btnCounter = new Button();
            lbl_Progress = new Label();
            SuspendLayout();
            // 
            // btnCounter
            // 
            btnCounter.Location = new Point(337, 86);
            btnCounter.Name = "btnCounter";
            btnCounter.Size = new Size(126, 45);
            btnCounter.TabIndex = 0;
            btnCounter.Text = "0";
            btnCounter.UseVisualStyleBackColor = true;
            btnCounter.Click += btnCounter_Click;
            // 
            // lbl_Progress
            // 
            lbl_Progress.Font = new Font("Microsoft YaHei UI", 12F, FontStyle.Regular, GraphicsUnit.Point);
            lbl_Progress.Location = new Point(300, 185);
            lbl_Progress.Name = "lbl_Progress";
            lbl_Progress.Size = new Size(200, 25);
            lbl_Progress.TabIndex = 1;
            lbl_Progress.Text = "开始执行";
            lbl_Progress.TextAlign = ContentAlignment.MiddleCenter;
            // 
            // Form1
            // 
            AutoScaleDimensions = new SizeF(9F, 20F);
            AutoScaleMode = AutoScaleMode.Font;
            ClientSize = new Size(800, 450);
            Controls.Add(lbl_Progress);
            Controls.Add(btnCounter);
            Name = "Form1";
            Text = "Form1";
            ResumeLayout(false);
        }

        #endregion

        private Button btnCounter;
        private Label lbl_Progress;
    }
}

这些代码是写界面的,为了简单,界面上只有两个控件:
在这里插入图片描述
在Form1.cs中改成以下代码:

using System;
using System.Windows.Forms;
using System.Threading;
using System.Threading.Tasks;

namespace WinFormsApp1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void btnCounter_Click(object sender, EventArgs e)
        {
            //第一次点击按钮时,执行方法
            if (btnCounter.Text == "0")
            {
                //做一些耗时操作
                DoSomething();
            }

            //点击按钮后累加计数
            btnCounter.Text = (int.Parse(btnCounter.Text) + 1).ToString();
        }

        /// <summary>
        /// 做一些耗时操作
        /// </summary>
        private void DoSomething()
        {
            //1.等待1秒
            Thread.Sleep(1000);
            //显示进度
            lbl_Progress.Text = "1.等待1秒 已完成";
            //2.耗时操作
            Thread.Sleep(100 * Random.Shared.Next(5, 20));
            //显示进度
            lbl_Progress.Text = "2.耗时操作 已完成";
            //3.等待1秒
            Thread.Sleep(1000);
            //显示进度
            lbl_Progress.Text = "3.等待1秒 已完成";

        }

    }
}

  运行程序后,点击一下按钮,执行耗时操作。我们会发现,界面被完全卡死,用户体验非常不好。下面我们使用异步执行耗时操作。

2.异步执行操作

将Form1.cs中,引入命令空间:

using System.Threading.Tasks;

将DoSomething函数改成如下代码:

/// <summary>
/// 做一些耗时操作
/// </summary>
private void DoSomething()
{
    //异步执行操作
    Task.Factory.StartNew(() =>
    {
        //1.等待1秒
        Thread.Sleep(1000);
        this.BeginInvoke((MethodInvoker)delegate ()
        {
            //显示进度
            lbl_Progress.Text = "1.等待1秒 已完成";
        });
        //2.耗时操作
        Thread.Sleep(100 * Random.Shared.Next(5, 20));
        this.BeginInvoke((MethodInvoker)delegate ()
        {
            //显示进度
            lbl_Progress.Text = "2.耗时操作 已完成";
        });
        //3.等待1秒
        Thread.Sleep(1000);
        this.BeginInvoke((MethodInvoker)delegate ()
        {
            //显示进度
            lbl_Progress.Text = "3.等待1秒 已完成";
        });
    });
}

  再次执行程序,发现,程序一边执行耗时操作,一边将耗时操作的过程显示到UI中。这样用户体验就很好了。

3.代码解析

  Task.Factory.StartNew(() => {});这句话,是开始一个新的线程执行操作,这样就不会卡死UI了。而在这个线程中操作UI是不行的,因为UI只能存在在一个线程中,即UI线程(也是主线程)。这时如果要进行UI显示,则需要用到this.BeginInvoke((MethodInvoker)delegate (){});这个函数将一段代码传递给UI线程,当UI线程进入到下一次消息循环时,就会找个时间,将这段代码执行了,通常一次消息循环非常快(0.1秒都不到)。这时,UI线程可以正常地显示UI了,同时UI线程还可以执行一些其他的非耗时操作,用户体验感很好。

  • 4
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Winform中,更新UI是一个常见的需求。通常情况下,UI的更新是在主线程(也称为UI线程)中完成的。但某些情况下,当我们需要进行一些耗时操作时,如果在主线程中进行,可能会导致UI的卡顿或无响应。这时就需要使用异步更新UI的方式。 异步更新UI可以通过多线程或者使用异步操作方法来实现。其中,通过使用异步操作方法是比较常见的方式。 在Winform中,可以使用async和await关键字来定义异步操作。在函数或方法前面使用async关键字进行标识,然后在需要异步执行的代码块前使用await关键字进行标识。这样就可以将这部分代码放在一个单独的任务中,不会影响主线程的执行。 以一个例子来说明,假设我们需要在按钮点击时执行一个耗时操作,然后将结果显示在Label控件上。 首先,我们需要在按钮的Click事件处理程序中标识为异步操作: ``` private async void Button_Click(object sender, EventArgs e) { await Task.Run(() => { // 执行耗时操作,例如数据库查询或网络请求 string result = SomeLongRunningOperation(); // 在UI线程中更新UI Invoke(new Action(() => { labelResult.Text = result; })); }); } ``` 在上述代码中,我们使用Task.Run方法创建了一个后台任务,并将耗时操作代码放在其中。然后,在任务完成后通过Invoke方法将结果更新到UI控件中。Invoke方法将会将UI操作投递到UI线程执行,确保线程安全性。 通过这种方式,当点击按钮时,耗时操作将在后台执行,不会阻塞主线程,保持UI的流畅性。同时,等待任务完成后,会将结果更新到UI控件上,实现异步更新UI的效果。 需要注意的是,在异步更新UI时,应该确保对UI线程的访问是线程安全的,可以使用Invoke或BeginInvoke等方法来实现。否则可能会导致线程冲突或其他问题。 总结起来,Winform异步更新UI可以通过使用async和await关键字,结合Task.Run和Invoke等方法来实现。这样可以在耗时操作完成后,将结果异步更新到UI上,提升用户体验。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

高思宇

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值