C# 通过PostMessage完成UI的更新
因为项目需要,最近一直在研究C#的UI的更新问题,在线程里更新UI会引发异常,一般情况采用Invoke/BeginInvoke方法来实现子线程对UI的更新。在这里我主要介绍通过PostMessage方法来完成跨线程UI更新。
一般情况下都采用代理的方式进行UI更新,当在子线程中需要更新窗体UI的时候,在子线程里可以定义一个委托,调用主线程的委托函数。如下所示:
1 public class TestThread 2 { 3 4 public delegate void UpdateUI(Object);//可以调用这个代理来激发窗口 5 6 public Thread SubThread ;//你的线程 7 8 public void SetUI(UpdateUI ui) 9 { 10 updateUI += ui; //设置UI更新函数,主窗体可以调用这个来注册一个代理到线程上去. 11 } 12 13 private UpdateUI updateUI; 14 15 //.... 16 private void Run() 17 { 18 //.... 19 updateUI(obj);//调用UI更新 20 ... 21 22 } 23 }
而在你的窗体里可以实现一个Updateui的函数,其简单的方法如下:
1 private delegate void WinfrmUpdate(Object obj);//定义委托 2 3 private WinfrmUpdate uptfuc;//定义一个给自己用的委托,需要将uptfuc指向ShowUpdate函数自己. 4 5 public void ShowUpdate(Object obj)//定义一个更新UI的函数 6 { 7 if (InvokeRequired)//是否需要Invoke 8 { 9 this.Invoke(uptfuc,obj);//Invoke切换线程. 10 } 11 else 12 { 13 //.... 14 //....做UI更新的事情... 15 } 16 17 }
本文的PostMessage方法也可以实现UI的更新,它的效率相对来说会更高。如下所示:
首先引入PostMessage方法:
PostMessage的函数原型如下所示:
1 [DllImport("user32.dll", EntryPoint = "PostMessage")] 2 public static extern int PostMessage(IntPtr hwnd, int Msg, int wParam, int lParam);
hwnd是窗口的句柄,Msg是消息类型,wParam为参数1,lParam为参数2
一般情况下,微软定义了0x0400是用户自定义消息的起始点,低于0x0400的都是系统的消息,这一点需要注意。
在子线程类中,我们可以通过调用PostMessage方法向主窗体发送消息:
public class TestThread { public const int WM_THREAD = 0x0402; private int count = 0; [DllImport("user32.dll", EntryPoint = "SendMessage")] public static extern int SendMessage(IntPtr hwnd, int Msg, int wParam, int lParam); [DllImport("user32.dll", EntryPoint = "PostMessage")] public static extern int PostMessage(IntPtr hwnd, int Msg, int wParam, int lParam); IntPtr hwnd; public Thread thd; public TestThread(IntPtr hd) { hwnd = hd; } public void Start() { thd = new Thread(new ThreadStart(Threads)); thd.IsBackground = true; thd.Start(); Console.WriteLine("线程启动."); } private void Threads() { while (true) { Thread.Sleep(500); PostMessage(hwnd, WM_THREAD, 0, count); count++; } } }
而在主窗体中,则可以通过改写WndProc函数来截获子线程发送的消息,如下图所示:
1 protected override void WndProc(ref Message m) 2 { 3 if (m.Msg == TestThread.WM_THREAD) 4 { 5 LabelText.Text = m.LParam.ToString(); 6 return; 7 } 8 base.WndProc(ref m); 9 }
如此一来,线程不需要时常通过代理切换来更新UI,对于一些处理比较频繁的线程来说,这不失为一种可以使用的选择。