zoukankan      html  css  js  c++  java
  • c#winform线程间操作UI的五种方法

    经常因为需要在线程间操作UI而头疼,总结了一下,记录出来,以后方便查阅。

    方法一

    通过设置窗体属性,取消线程间的安全检查。(最简单,最省事,也是最不负责任的一种)

     1 public partial class one : Form
     2     {
     3         public one()
     4         {
     5             InitializeComponent();
     6             Control.CheckForIllegalCrossThreadCalls = false;//取消线程间的安全检查
     7         }
     8 
     9         private void Form1_Load(object sender, EventArgs e)
    10         {
    11             Thread listen = new Thread(new ThreadStart(receive));
    12             listen.IsBackground = true;
    13             listen.Start();
    14         }
    15         private void receive()
    16         {
    17             UdpClient uc = new UdpClient(5839);
    18             while (true)
    19             {
    20                 IPEndPoint ip = null;
    21                 byte[] message = uc.Receive(ref ip);
    22                 string messagestring = Encoding.UTF8.GetString(message);
    23                 textBox1.Text = messagestring;
    24             }
    25         }
    26 
    27         private void button1_Click(object sender, EventArgs e)
    28         {
    29             UdpClient uc = new UdpClient("192.168.0.53",5839);
    30             string messagestring = "改变啦!";
    31             byte[] message = Encoding.UTF8.GetBytes(messagestring);
    32             uc.Send(message,message.Length);
    33             uc.Close();
    34         }
    35     }

    上述代码,就是在一个窗体内,本窗体给本窗体通过udp发送消息。接收线程接到发来的消息后,使窗体的UI发生改变。效果图如下:

    这种方法,可能会导致不安全,不推荐使用。

    方法二

    通过设置全局变量属性,利用timer模拟实现此效果。

     1 public partial class two : Form
     2     {
     3         string messagestring = "";
     4         public two()
     5         {
     6             InitializeComponent();
     7         }
     8 
     9         private void two_Load(object sender, EventArgs e)
    10         {
    11             Thread listen = new Thread(new ThreadStart(receive));
    12             listen.IsBackground = true;
    13             listen.Start();
    14             timer1.Start();
    15         }
    16         private void receive()
    17         {
    18             UdpClient uc = new UdpClient(5839);
    19             while(true)
    20             {
    21                 IPEndPoint ip = null;
    22                 byte[] message = uc.Receive(ref ip);
    23                 messagestring = Encoding.UTF8.GetString(message);
    24             }
    25         }
    26 
    27         private void button1_Click(object sender, EventArgs e)
    28         {
    29             UdpClient uc = new UdpClient("127.0.0.1",5839);
    30             string message = "改变啦!";
    31             byte[] send = Encoding.UTF8.GetBytes(message);
    32             uc.Send(send,send.Length);
    33             uc.Close();
    34         }
    35 
    36         private void timer1_Tick(object sender, EventArgs e)
    37         {
    38             textBox1.Text = messagestring;
    39         }
    40     }

    此方法的原理是,当接收线程接收到发来的消息后,将消息赋值到一个全局变量上,同时timer一直在运行textbox1.text = messagestring;

    UI的改变交给timer来实现。效果图如下。

    这种方法,不推荐使用,占用资源过多,并且根据timer时间设置的不同会有不同的延时。

    方法三

    通过winform自带的backgroundworker取代thread进行异步操作。

     1 public partial class three : Form
     2     {
     3         public three()
     4         {
     5             InitializeComponent();
     6         }
     7 
     8         private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
     9         {
    10             UdpClient uc = new UdpClient("127.0.0.1",5839);
    11             string messagestring = "改变啦!";
    12             byte[] message = Encoding.UTF8.GetBytes(messagestring);
    13             uc.Send(message,message.Length);
    14             uc.Close();
    15         }
    16         private void button1_Click(object sender, EventArgs e)
    17         {
    18             backgroundWorker1.RunWorkerAsync();
    19         }
    20 
    21         private void three_Load(object sender, EventArgs e)
    22         {
    23             backgroundWorker2.RunWorkerAsync();
    24         }
    25 
    26         private void backgroundWorker2_DoWork(object sender, DoWorkEventArgs e)
    27         {
    28             UdpClient uc = new UdpClient(5839);
    29             while(true)
    30             {
    31                 IPEndPoint ip = null;
    32                 byte[] message = uc.Receive(ref ip);
    33                 string messagestring = Encoding.UTF8.GetString(message);
    34                 backgroundWorker2.ReportProgress(50, messagestring);
    35             }
    36         }
    37 
    38         private void backgroundWorker2_ProgressChanged(object sender, ProgressChangedEventArgs e)
    39         {
    40             textBox1.Text = e.UserState.ToString();
    41         }
    42 
    43     }

    上面代码的原理就是,把对UI的操作放到了backgroundworker2_progresschanged方法中进行,winform自带的backgroundworker不会受到影响,可以对UI进行操作。

    效果图如下

    这种方法,不推荐使用,虽然并没有什么不好的。但是就是感觉特别挫,因为只局限于winform,到了其它的地方,还不是得用thread来实现,使用这种方法解决问题,治标不治本。

    方法四

    通过UI线程的SynchronizationContext的Post/Send方法更新,代码如下

     1 public partial class fourth : Form
     2     {
     3         SynchronizationContext SyncContext = null;
     4         public fourth()
     5         {
     6             InitializeComponent();
     7             SyncContext = SynchronizationContext.Current;
     8         }
     9 
    10         private void fourth_Load(object sender, EventArgs e)
    11         {
    12             Thread listen = new Thread(new ThreadStart(receive));
    13             listen.IsBackground = true;
    14             listen.Start();
    15         }
    16         private void receive()
    17         {
    18             UdpClient uc = new UdpClient(5839);
    19             while (true)
    20             {
    21                 IPEndPoint ip = null;
    22                 byte[] message = uc.Receive(ref ip);
    23                 string messagestring = Encoding.UTF8.GetString(message);
    24                 SyncContext.Post(change,messagestring);
    25             }
    26         }
    27         private void change(object str)
    28         {
    29             textBox1.Text = str.ToString();
    30         }
    31         private void button1_Click(object sender, EventArgs e)
    32         {
    33             UdpClient uc = new UdpClient("127.0.0.1", 5839);
    34             byte[] message = Encoding.UTF8.GetBytes("改变啦!");
    35             uc.Send(message, message.Length);
    36             uc.Close();
    37         }
    38     }

    原理是,在线程执行过程中,需要更新到UI控件上的数据不再直接更新,而是通过UI线程上下文的Post/Send方法,将数据以异步/同步消息的形式发送到UI线程的消息队列;UI线程收到该消息后,根据消息是异步消息还是同步消息来决定通过异步/同步的方式调用SetTextSafePost方法直接更新自己的控件了。

    在本质上,向UI线程发送的消息并不是简单数据,而是一条委托调用命令。效果图如下

    这种方法,推荐使用,是不错的解决问题的好方法。

    方法五

    通过设置UI控件的Invoke和BeginInvoke方法实现更新,代码如下

     1 public partial class fifth : Form
     2     {
     3         delegate void Change(string text);
     4         public fifth()
     5         {
     6             InitializeComponent();
     7         }
     8         private void Settext(string text)
     9         {
    10             textBox1.Text = text;
    11         }
    12 
    13         private void fifth_Load(object sender, EventArgs e)
    14         {
    15             Thread listen = new Thread(new ThreadStart(receive));
    16             listen.IsBackground = true;
    17             listen.Start();
    18         }
    19         private void receive()
    20         {
    21             UdpClient uc = new UdpClient(5839);
    22             while (true)
    23             {
    24                 IPEndPoint ip = null;
    25                 byte[] message = uc.Receive(ref ip);
    26                 string messagestring = Encoding.UTF8.GetString(message);
    27                 this.BeginInvoke(new Change(Settext),messagestring);
    28             }
    29         }
    30 
    31         private void button1_Click(object sender, EventArgs e)
    32         {
    33             UdpClient uc = new UdpClient("127.0.0.1", 5839);
    34             byte[] message = Encoding.UTF8.GetBytes("改变啦!");
    35             uc.Send(message,message.Length);
    36             uc.Close();
    37         }
    38     }

    这个方法是目前跨线程更新UI使用的主流方法,使用控件的Invoke/BegainInvoke方法,将委托转到UI线程上调用,实现线程安全的更新。效果图如下

    这种方法推荐使用,是当前的主流。

    总结,多线程间会经常使用到委托,对委托的理解十分关键。

  • 相关阅读:
    [05] EL表达式
    [03-01] JSP自定义标签
    [04] JSP标准动作
    [03] JSP指令
    Fiddler抓包调试前端脚本代码
    《互联网协议入门》思维导图笔记
    Nodejs学习笔记(十)—与MongoDB的交互(mongodb/node-mongodb-native)、MongoDB入门
    Nodejs学习笔记(九)—与Redis的交互(mranney/node_redis)入门
    Nodejs学习笔记(八)—Node.js + Express 实现上传文件功能(felixge/node-formidable)
    Nodejs学习笔记(七)—Node.js + Express 构建网站简单示例
  • 原文地址:https://www.cnblogs.com/weifeng123/p/13734999.html
Copyright © 2011-2022 走看看