好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

跨线程修改UI控件

跨线程修改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.

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

作者: Leo_wl

    

出处: http://www.cnblogs.com/Leo_wl/

    

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

版权信息

查看更多关于跨线程修改UI控件的详细内容...

  阅读:36次