C# Winform 多线程更新界面UI控件,解决界面卡顿问题

前言

        多线程刷新界面主要用到多线程,委托,线程安全、事件等一系列高难度的C#操作。

1、使用timer控件对要刷新的控件进行定时刷新

        对刷新频率要求不高的时候,可以使用该方法。

2、刷新UI控件

        在开发软件时经常会需要更新界面上的某个控件来显示相关的信息,为了让程序执行中不出现界面卡死的现像,最好的方法就是“多线程+委托”来解决。

控件:pictureBox1

        public delegate void DeleMyDelegate();//定义委托
        DeleMyDelegate deleMyDelegate;//声明委托
        public void DeleMy()//定义信息函数
        {
            pictureBox1.Refresh();
        }
        public void Pic_Show()
        {
            deleMyDelegate = new DeleMyDelegate(DeleMy);//绑定委托
            this.pictureBox1.Invoke(deleMyDelegate);
        }
        
        private void button1_Click(object sender, EventArgs e)
        {
            Thread myThread = new Thread(Pic_Show);
            myThread.Start();
        }

控件:richTextBox1

        Thread myThread;
        public delegate void MyDelegateUI(); //定义委托类型
        MyDelegateUI myDelegateUI; //声明委托对象
        public Form1()
        {
            InitializeComponent();

            myThread = new Thread(doWork);
            myDelegateUI = new MyDelegateUI(initAll);//绑定委托
        }

        private void initAll() //信息处理函数定义
        {
            richTextBox1.AppendText("TEST line \r");
        }

        private void doWork()
        {
            for (int i = 0; i < 10; i++)
            {
                Thread.Sleep(100);
                this.Invoke(myDelegateUI); //richTextBox1.AppendText("TEST line \r");
                Application.DoEvents();
            }
        }

        private void button1_Click(object sender, EventArgs e)
        {
            myThread.Start();
        }

3、工作线程中刷新主界面控件状态

(1)工作线程类

    public class Work
    {
        private volatile static Work instanceWork;    // 单例

        public delegate void InvokeListBox(int i);
        public InvokeListBox invokeOthers;

        /// <summary>
        /// 构造函数
        /// </summary>
        public Work()
        {

        }

        /// <summary>
        /// 对外接口,获取单例
        /// </summary>
        /// <returns></returns>
        public static Work GetInstance()
        {
            if (null == instanceWork)
            {
                instanceWork = new Work();
            }

            return instanceWork;
        }

        /// <summary>
        /// 业务函数,在工作过程中将状态传给主界面
        /// </summary>
        public void DoSomething()
        {
            for (int i = 0; i < 20; i++)
            {
                Thread.Sleep(1000);
                invokeOthers(i);
            }
        }

    }

(2)主界面布局及代码

        private delegate void InvokeListBox(string strCon);    // 刷新ListBox的委托。
        private InvokeListBox invokeListBox;

        private Work wk;    //工作线程对象

        public Form1()
        {
            InitializeComponent();

            wk = Work.GetInstance();    //单例模式初始化工作线程对象
            wk.invokeOthers = new Work.InvokeListBox(ReciveData);    // 绑定,接收工作线程过来的数据
            invokeListBox = new InvokeListBox(RefrushListBox);       // 绑定,刷新界面ListBox控件
        }

        private void button1_Click(object sender, EventArgs e)
        {
            Thread th = new Thread(new ThreadStart(wk.DoSomething));
            th.Start();
        }

        /// <summary>
        /// 接收工作线程过来的数据,更新界面
        /// </summary>
        /// <param name="i"></param>
        public void ReciveData(int i)
        {
            string strConten = i.ToString() + "  更新";

            if (this.listBox1.InvokeRequired)
            {
                this.listBox1.Invoke(invokeListBox, new object[] { strConten });
            }
        }

        /// <summary>
        /// 具体刷新界面函数
        /// </summary>
        /// <param name="_str"></param>
        public void RefrushListBox(string _str)
        {
            this.listBox1.Items.Add(_str);
        }

(3)说明

        本例子中有两个委托:1、Work.cs中的委托变量 ivokeOthers,作用是将信息传递给给主界面对应的函数:ReceiveData()。2、Form_MainUI.cs中的委托变量 invokeListBox,作用是刷新界面控件ListBox_state的内容。点击“开始工作”按钮后,工作线程启动,调用Work.cs中DoSomeThing()函数。由于Form_MainUI.cs中的函数ReceiveData()函数绑定了Work.cs中的委托ivokeOthers,所以当Work.cs中DoSomeThing被调用时ivokeOthers会把变量i传递给ReceiveData()。ReceiveData()接收到数据后,通过Invoke调用刷函数RefrushListBox()修改主界面ListBox_State的内容。

(4)总结

        1)不要惧怕委托、事件的复杂的概念,动手操作练习一下,就简单的把它们当做传递消息的载体。

        2)在工作线程中修改主界面其它控件的内容、状态、图片等操作类似,可以自己动手试试。

        3)本小结在实现刷新主界面控件的时候使用了单例模式,不使用单例模式也完全可以实现,加上单例是为了让调用更加方便,在大项目开发中就会体现其优势。

 

  • 18
    点赞
  • 114
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
Winform 中,UI 控件只能在主线程中更新。如果在非主线程中更新 UI 控件,会抛出 "跨线程访问无效: 从不是创建控件的线程访问它" 的异常。因此,我们需要使用以下方式在分线程中更新 UI 控件: 1. 使用 Control.Invoke 方法 使用 Control.Invoke 方法可以将代码添加到 UI 线程的消息队列中执行。这样可以保证在 UI 线程中更新控件,避免跨线程访问异常。 示例代码: ``` private void button1_Click(object sender, EventArgs e) { Thread t1 = new Thread(new ThreadStart(UpdateTextBox)); t1.Start(); } private void UpdateTextBox() { if (textBox1.InvokeRequired) { textBox1.Invoke(new MethodInvoker(delegate { textBox1.Text = "Hello World"; })); } else { textBox1.Text = "Hello World"; } } ``` 2. 使用 BackgroundWorker 组件 BackgroundWorker 组件是一个用于执行异步操作的组件,在操作完成后可以安全地更新 UI 控件。它具有一个 ProgressChanged 事件和一个 RunWorkerCompleted 事件,可以在这两个事件中更新 UI 控件。 示例代码: ``` private void button1_Click(object sender, EventArgs e) { backgroundWorker1.RunWorkerAsync(); } private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) { // 在这里执行耗时的操作 } private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e) { // 在这里更新 UI 控件 } private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { // 在这里更新 UI 控件 } ``` 以上两种方式都可以在分线程中安全地更新 UI 控件,具体使用哪种方式取决于实际情况。
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值