winform自定义控件跨线程更新UI的疑问

//定义一个自定义控件,包含如下属性和方法,绑定此working属性到worker的working属性
bool _working = false; string _thread_name1 = ""; string _thread_name2 = ""; public bool working { get { return _working; } set { if (value != _working) { _working = value; //在这里把触发属性更改的线程名称记录下来 _thread_name1 = string.IsNullOrEmpty(System.Threading.Thread.CurrentThread.Name)?"null": System.Threading.Thread.CurrentThread.Name; //Invalidate(); Refresh(); } } } protected override void OnPaint(PaintEventArgs e) { base.OnPaint(e); //根据属性working的值绘背景颜色,用来监控属性确实被更改了 if (working) { e.Graphics.FillRectangle(Brushes.Green, ClientRectangle); } else { e.Graphics.FillRectangle(Brushes.Red, ClientRectangle); } //打印触发属性更改的线程名称 e.Graphics.DrawString(_thread_name1, Font, Brushes.Black,0,0); //打印当前(执行重绘方法)线程名称 _thread_name2 = string.IsNullOrEmpty(System.Threading.Thread.CurrentThread.Name) ? "null" : System.Threading.Thread.CurrentThread.Name; e.Graphics.DrawString(_thread_name2, Font, Brushes.Blue, 0, Height/2); }
//再定义一个worker类,在start()/stop()方法里更改属性working
public class worker : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; bool _working = false; public bool working { get { return _working; } set { if (value != _working) { _working = value; if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs("working")); } } } } Thread _work_thread; bool _release_work = false; public void start_work() { if (_work_thread == null || _work_thread.ThreadState != ThreadState.Running) { _release_work = true; _work_thread = new Thread(work); _work_thread.IsBackground = true; _work_thread.Start(); } _work_thread = new Thread(work); _work_thread.IsBackground = true; _work_thread.Name = "working thread"; //Thread.CurrentThread.Name = "main thread"; _work_thread.Start(); } public void stop_work() { _release_work = false; _working = false; } public void work() { while(_release_work) { working = true; Thread.Sleep(200); if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs("workdone")); } working = false; } }

这个时候就发现,触发自定义控件属性更改的实践线程是不固定的,有时候是主线程,有时候是辅线程,自定义控件在重绘的时候从来不报跨线程错误!而同样绑定working属性的其他winfrom控件立刻报错。这是为什么呢?折腾了好久找到后面的答案。实验的一下确实如此,onpaint的线程一定是主线程。如果换成refresh()方法重绘控件,照样立刻报跨线程错误。

MSDN:
调用 Invalidate 方法并不强制同步绘制;若要强制同步绘制,请在调用 Invalidate 方法之后调用 Update 方法。在不带参数的情况下调用此方法时,会将整个工作区添加到更新区域。

这句话的意思是如果我在自定义的方法中调用了Invalidate,那么相当于我告诉程序,当前的页面无效,你必须给我重新绘制,但是不是立即重新绘制,而是放置一个WM_PAINT消息到消息队列中,操作系统收到这个消息后才能绘制窗体。

不管怎样,系统肯定会重新绘制调用Invalidate的窗体或者控件对象。
---------------------
作者:阿达King哥
来源:CSDN
原文:https://blog.csdn.net/JimFire/article/details/41206413
版权声明:本文为博主原创文章,转载请附上博文链接!

猜你喜欢

转载自www.cnblogs.com/franklin2018/p/10700739.html