Michael's

focus on architecture & hi-performance

用Windows消息机制解决跨线程添加子控件异常

写过Winform程序的同学或许都遇到过这样的异常: 

Title

在某个线程上创建的控件不能成为在另一个线程上创建的控件的父级

 遇到这个问题之后,第一反应当然是g一把,解决方案大多数都是类似这样的代码:

 1 if(XXX.InvokeRequired)
 2 {
 3   //...
 4 }
 5 else
 6 {
 7   //...
 8 }

这段代码的原理是将

1 ControlCollection.Add(Control); 

转移到主线程中执行,貌似能解决哦?但是很多情况下并不能解决根本问题,异常照样会抛出来,具体原因我也不得而知,但是至少我们从可以绕道而行之...

上文提到在主线程中添加子控件的原理,那么我们可以从此下手,很显然,windows消息的触发一定是在主线程中执行的,在尝试之后我总结了2种情况:

1.简单子控件

如果只需要添加个按钮啥的,可以直接利用父控件的Paint消息,因为很简单,要注意的是Paint消息会不定时触发,需避免重复添加子控件

2.复杂子控件

如果子控件中包含各种COM控件,而且还包含耗时的业务调用,这时就得小心了,如果继续在Paint消息中处理,很可能会导致界面刷新不及时,而且这种方式确实有点山寨...

 

那具体要怎样做呢?

  1. 调用Windows API之PostMessage(Intptr, int, int, int)向某窗口发送消息,当然SendMessage亦可
  2. 一般情况下,该窗口对象可以重写WndProc方法分发该消息,但是还有更优雅的方式:继承IMessageFilter接口,拦截所有的Windows消息进行分发
  3. 处理类对象接收到对应的windows消息后创建子控件对象后添加

OK,总结吧

 

优点:很显然,无需担心COM控件的创建以及方法调用,实乃居家旅行杀人越货之良品
缺点:需要创建诸多的类成员变量(不优雅),以及需要仔细控制方法调用顺序,更可能需要对已有的代码结构进行仔细的重构,很好很山寨...

posted on 2010-09-20 12:59 m.s 阅读(1755) 评论(9) 编辑 收藏

Feedback

#1楼 2010-09-20 13:07 %admin      

.TopLevel
 回复 引用 查看   

#2楼 2010-09-20 16:02 Galactica      

你这不又回到MFC时代了,一个WinProc,然后一堆
case WM_USER_XXXX:{}break;
 回复 引用 查看   

#3楼[楼主] 2010-09-20 16:33 micYng      

引用Galactica:
你这不又回到MFC时代了,一个WinProc,然后一堆
case WM_USER_XXXX:{}break;


不能说完全回到MFC时代,理由如下:
1.通过IMessageFilter拦截消息,而不是WndProc
2.MFC时代用param传递变量地址,至少这种不是
3.这种方法使用范围不会很大,跨线程创建控件的机会不多

这么做也是不得已而为之...  回复 引用 查看   

#4楼 2010-09-20 17:44 徐少侠      

MFC是必杀技
在没有想到其他方法之前,该用还是要用的
 回复 引用 查看   

#5楼 2010-09-20 17:52 Galactica      

HWnd 获取或设置消息的窗口句柄。
LParam 指定消息的 LParam 字段。
Msg 获取或设置消息的 ID 号。
Result 指定为响应消息处理而向 Windows 返回的值。
WParam 获取或设置消息的 WParam 字段。

看下Message结构,或许哪天你又觉得使用LParam传递变量地址挺方便的.
 回复 引用 查看   

#6楼[楼主] 2010-09-20 18:01 micYng      

引用Galactica:
HWnd 获取或设置消息的窗口句柄。
LParam 指定消息的 LParam 字段。
Msg 获取或设置消息的 ID 号。
Result 指定为响应消息处理而向 Windows 返回的值。
WParam 获取或设置消息的 WParam 字段。

看下Message结构,或许哪天你又觉得使用LParam传递变量地址挺方便的.


如果你非得这样说,在c#中把消息机制套进来也未尝不可,只是有点得不偿失了  回复 引用 查看   

#7楼 2010-09-21 10:42 陈梓瀚(vczh)      

创建和添加都得在同一个线程里面完成。如果你创建在非UI线程,添加在UI线程那也是不行的。  回复 引用 查看   

#8楼[楼主] 2010-09-21 11:38 micYng      

引用陈梓瀚(vczh):创建和添加都得在同一个线程里面完成。如果你创建在非UI线程,添加在UI线程那也是不行的。


起初我还真是都在非UI线程中创建和添加的...

bug啊bug  回复 引用 查看   

#9楼 2010-09-21 17:36 陈梓瀚(vczh)      

@micYng
因此你很有可能要Invoke两次。
 回复 引用 查看   

导航

统计信息

News

搜索

 
 

常用链接

我的标签

随笔分类

随笔档案

文章分类

相册

.Net Enterprise Library

.Net Remoting

ASP.NET

Blog Friends:)

C# category

C# forum&blogs

C# Toolkit

CodeSmith Usages

cPP related

Design Pattern

Opensource project

Pervious Blog

Useful tip

积分与排名

最新评论

阅读排行榜

评论排行榜

推荐排行榜