zoukankan      html  css  js  c++  java
  • 跨线程修改UI控件

    跨线程修改UI控件

    前些天,有个朋友让我给他演示下C#,好快速入门,以完成某科目的期末大作业。然后,就涉及到一个进度条的东西,紧接着就涉及到跨线程修改控件的问题了。

    因为从来没用过,所以,华丽丽的跪了。只在网上搜到一个异步的方式,但是,此异步是使用控件的异步,仍然会导致UI线程的卡顿。

    经过几天的寻找资料,大致可以得到下面几种方法:

    1.直接在线程创建线程中,对是否是创建线程访问控件不做检查,直接跨线程操作。

    首先,在创建线程中,添加语句:

     CheckForIllegalCrossThreadCalls = false;
    
    然后,定义按钮事件:
     
    private void startButton_Click(object sender, EventArgs e)
            {
                CheckForIllegalCrossThreadCalls = false;
                progressBar1.Maximum = 50;
                myThread = new Thread(canCrossThreadCall); myThread.Start();
            }

    这里习惯性的自己控制线程。下面是线程中的操作,以及结束按钮事件:

    复制代码
    private void canCrossThreadCall()
            {
                progressBar1.Maximum = 50;
                for (int i = 0; i < 50; i++)
                {
                    Thread.Sleep(500);
                    progressBar1.Value = i + 1;
                }
            }
    
            private void endButton_Click(object sender, EventArgs e)
            {
                if (myThread != null)
                {
                    myThread.Abort(); myThread = null;
                    progressBar1.Value = 0;
                }
               
            }
    复制代码

    由于设置了CheckForIllegalCrossThreadCalls = false; 所以,跨线程的操作被允许了,如果没有设置,默认是不允许的,这样会导致抛出异常。

    2.使用上下文环境进行操作

    这样的操作方式,估计对于问这个问题的人来说,应该是最容易理解的。毕竟他本身是一个Androider。。。Handler类。。。当时受启发,想着我可以利用sendmessage或者postmessage的,但是。。。。不知道为什么sendmessage只接受到了第一次的消息。。。然后,还是使用C#自身的东西吧,避免与操作系统挂钩

    具体代码如下:

    复制代码
    //上下文环境
            SynchronizationContext sc;
            private void nStartButton_Click(object sender, EventArgs e)
            {
                progressBar1.Maximum = 50;
                myThread = new Thread(notCanCrossThreadCall);
                myThread.Start(50);
                //获取上下文环境
                sc = SynchronizationContext.Current;
            }
    
            private void notCanCrossThreadCall(object m)
            {
                int max = (int)m;
                for (int i = 0; i < max; i++) 
                {
                    Thread.Sleep(500);
                    //通过上下文环境发送消息,从而设置进度条
                    sc.Post(setValues, i + 1);
                }
            }
            private void setValues(object i)
            {
                progressBar1.Value = (int)i;
                if ((int)i == progressBar1.Maximum)
                    MessageBox.Show("操作完成");
            }
    
            private void nEndButton_Click(object sender, EventArgs e)
            {
                if (myThread != null) 
                {
                    myThread.Abort();
                    myThread = null;
                    progressBar1.Value = 0;
                }
            }
    复制代码

    3.使用基于事件的异步操作(BackGroundWorker组件)

    当时查资料的时候,说到这个的最多。然后,也自然的最近看了下一些人写的异步操作的,从1-4都看完了~不得不说,文章还是可以的。。。

    向form中拖一个BackGroundWorker组件,然后,设置对应的三个事件:

    复制代码
    private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
            {
                BackgroundWorker bgWorker = sender as BackgroundWorker;
                for (int i = 0; i < 50; i++)
                {
                    if (bgWorker.CancellationPending) { e.Cancel = true; break; }
                    Thread.Sleep(500);
                    bgWorker.ReportProgress(i+1);
                }
            }
    
            private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
            {
                this.progressBar1.Value = e.ProgressPercentage;
            }
    
            private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
            {
                if (e.Cancelled)
                {
                    MessageBox.Show("操作被取消");
                }
                else
                {
                    MessageBox.Show("操作完成");
                }
            }
    复制代码

    然后,再设置开始和结束按钮的事件:

    复制代码
    private void bgStartButton_Click(object sender, EventArgs e)
            {
                if (backgroundWorker1.IsBusy != true)
                {
                    progressBar1.Maximum = 50;
                    backgroundWorker1.WorkerReportsProgress = true;
                    backgroundWorker1.WorkerSupportsCancellation = true;
                    backgroundWorker1.RunWorkerAsync();
                }
                else 
                {
                    MessageBox.Show("busy");
                }
            }
            private void bgEndButton_Click(object sender, EventArgs e)
            {
                if (backgroundWorker1.IsBusy && backgroundWorker1.WorkerSupportsCancellation == true)
                    backgroundWorker1.CancelAsync();
            }
    复制代码

    这里,当RunWorkerAsync被调用,就会产生Do_Work事件,而Do_Work事件中,可以根据需要调用ProgressChanged事件,从而达到进度报告的关系。在异步进程结束的时候,会触发RunWorkerCompleted事件。

    ***********************************分割线***********************************

    以上三种,均可以完成跨线程的操作,不会导致UI界面的假死状态。

    其实,第二种可以使用异步中的APM来操作,使用委托来完成。

    这样一路搞下来,尤其是在看了第三种的源码,以及第二种使用委托进行异步的原理,感觉上,最基本的就是线程,委托等操作,然后,第三种有种第二种封装的结果。从而给开发者方便,可以高效快速的完成开发工作。

    而且,跨线程的操作UI,对于第一种不予评论,是实实在在的跨线程了。

    而对于后2种,都是将修改的操作,交给了UI线程去做,通过委托,事件去完成。都是向UI线程发送消息,然后UI线程去修改界面。

    P.S.

    人生的第一次实习生笔试华丽丽的没了。。。。祝好运吧

  • 相关阅读:
    ios---图片缩放
    ios---清除缓存
    ReactNative---组件种类
    Linux 用户和用户组管理
    Linux 文件与目录管理
    linux文件的基本属性
    xshell 快速复制打开之前用过的ssh
    ll 和 ls -l的详解
    laravel rbac的用户 角色 权限的crud
    laravel 中的rbac自己简单的实现
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/3083195.html
Copyright © 2011-2022 走看看