解说Win32的窗口子类化

代码下载:subclass(请点击进入论坛回复以后下载)

也许你需要一个特殊的Edit来限制浮点数的输入,但是现有的Edit却并不能完成这项工作――因为它只能够单纯的限制大小写或者纯数字。当你在论坛上求救的时候,某个网友告诉你:“用子类化。”你也许会在看到一线曙光的同时多出了一连串的问题:何为子类化?子类化的原理是什么?如何实现子类化?下面就让我从一个简单的C++程序开始,一步步解开你的疑团吧。 
 首先,我为你列出以下这个C++程序:

01. #include <iostream>
02. using  namespace  std;
03. class  Parent
04. {
05. public :
06. void  func( void ) { cout <<  "func of Parent"  << endl; }
07. };
08. class  Child :  public  Parent
09. {
10. public :
11. void  func( void ) { cout <<  "func of Child"  << endl; }
12. };
13. void  main()
14. {
15. Parent p;
16. Child c;
17. p.func();
18. c.func();
19. }</iostream>

现在我来解说一下。这段代码中我定义了两个C++类:父类和子类,并且子类是继承自父类的;它们有一个具有相同名称的成员函数func。在main函数中,我分别构造了父类和子类的对象,并调用了它们各自的成员函数func。结果如下:

1. func of Parent
2. func of Child

简单说来,这段代码就是子类根据自己的需要改写了func成员函数。而Win32的子类化的原理也与此类似,只不过子类化实际上并没有像C++一样重载哪个函数,而是靠拦截Windows系统中的某些消息来自己进行处理罢了。举例来说,请大家看以下这段简单的窗口回调过程:

01. LRESULT  CALLBACK ProcMain( HWND  hDlg,  UINT  Msg,  WPARAM  wParam,  LPARAM lParam)
02. {
03. switch  (Msg)
04. {
05. case  WM_CLOSE:
06. EndDialog(hDlg, 0);
07. break ;
08. case  WM_DESTROY:
09. PostQuitMessage(0);
10. break ;
11. }
12. return  0;
13. }

在这个回调之中,我手动处理了两个消息:在单击了“关闭”按钮(WM_CLOSE)的时候,我将对话框关闭(EndDialog);在对话框销毁(WM_DESTROY)的时候,我向系统消息队列中发送了退出的消息来完成结束工作(PostQuitMessage)。也就是说,如果把WM_CLOSE的响应代码改成:

1. case  WM_CLOSE:
2. ShowWindow(hDlg, SW_MINIMIZE);
3. break ;

这样一来,这个对话框就会和MSN一样,在单击了“关闭”之后,就会完成最小化的工作了。那么,对于窗口过程已定义好的系统控件,将如何手动响应它的消息呢? 
   我们可以用函数指针的办法,将我们感兴趣的消息拦截下来,处理完之后再让预定义的窗口过程处理。这个过程大致如下:

1. WNDPROC OldProc;
2.   OldProc = (WNDPROC)SetWindowsLong(hWnd, GWL_WNDPROC, ( LONG )NewProc);

当然,这里的新窗口过程NewProc是预先由你实现好的。上述代码执行以后,系统在处理hWnd的窗口消息时,就会先进入你实现的NewProc回调过程,然后在处理过你感兴趣的消息之后,通过CallWindowProc函数和你预先保存的OldProc再次回到原来的回调过程中完成剩余的工作。 
   以上就是窗口子类化的原理分析,下面我通过一个实例来实际解说如何对窗口进行子类化。

这个例子的界面如下:

 52 解说Win32的窗口子类化

界面上方的编辑框是用来限制浮点输入的,下面则是一个普通的超级链接。 
   好了,下面我开始按步骤完成对这两个窗口的子类化: 
   第一步,在主窗口对话框初始化的时候,保存原有的窗口过程,并设置新的窗口过程。代码如下:

1. case  WM_INITDIALOG:
2. EditProc = (WNDPROC)SetWindowLong(GetDlgItem(hDlg, IDC_EDIT), GWL_WNDPROC, ( LONG )ProcFloat);
3. StaticProc = (WNDPROC)SetWindowLong(GetDlgItem(hDlg, IDC_ST_HOMEPAGE), GWL_WNDPROC, ( LONG )ProcLink);
4. break ;

第二步,实现浮点编辑框的窗口过程:

01. LRESULT  CALLBACK ProcFloat( HWND  hWnd,  UINT  Msg,  WPARAM  wParam,  LPARAM lParam)
02. {
03. if  (Msg == WM_CHAR && wParam !=  '' . ''  && (wParam <=  '' 0 ''  || wParam >=  '' 9 '' ) && wParam != VK_BACK)
04. {
05. MessageBeep(MB_OK);
06. return  0;
07. }
08. else
09. return  CallWindowProc(EditProc, hWnd, Msg, wParam, lParam);
10. }

这里需要解释的是,由于控件本身的需求,所以只需要拦截一个消息,就是接收字符的WM_CHAR。当用户输入的字符不是小数点、0~9以及退格键(注意不要少了退格键,否则你将会发现你的编辑框无法删除输入错误的数字)的时候,就发出一声声音以提示输入错误。至于其它的消息,则调用原有的回调函数进行处理。 
   第三步,实现超级链接的窗口过程:

01. LRESULT  CALLBACK ProcLink( HWND  hWnd,  UINT  Msg,  WPARAM  wParam,  LPARAM lParam)
02. {
03. switch  (Msg)
04. {
05. case  WM_SETCURSOR:
06. SetCursor(LoadCursor(NULL, IDC_HAND));
07. break ;
08. case  WM_LBUTTONDOWN:
09. ShellExecute(NULL,  "open" "http://home.ncust.edu.cn/~titilima" , NULL, NULL, SW_SHOWNORMAL);
10. break ;
11. default :
12. return  CallWindowProc(StaticProc, hWnd, Msg, wParam, lParam);
13. }
14. return  0;
15. }

这段代码很容易明白:它完成了两件事,其一是设置光标指针为手形(注意:对于较早的Windows系统,是没有预定义的IDC_HAND指针的,这个时候你需要在EXE的资源中自己画一个手形指针,比如Delphi之中的手形指针就是自己画的),其二是当单击了鼠标左键的时候打开你想打开的网页链接。 
   其实对于超级链接,它更主要的东西是在子类化之外实现的――就是它的字体颜色(注意这段代码是在主窗口对话框的回调过程中实现的):

1. case  WM_CTLCOLORSTATIC:
2. if  (GetDlgItem(hDlg, IDC_ST_HOMEPAGE) == ( HWND )lParam)
3. {
4. SetTextColor(( HDC )wParam, 0xff0000);
5. SetBkMode(( HDC )wParam, TRANSPARENT);
6. return  ( LRESULT )CreateSolidBrush(GetSysColor(COLOR_BTNFACE));
7. }
8. break ;

还有几点要说明的是:
1、你的这个Static超链接必须拥有一个唯一的资源ID,比如我的这个就是IDC_ST_HOMEPAGE,这样才能用GetDlgItem获得它的句柄来完成子类化;
2、你必须为它设置SS_NOTIFY样式,以保证在单击它的时候它能够通知父窗口对话框;
3、单击它打开网页的处理也可以放在子类化之外,处理主窗口对话框的WM_COMMAND消息也可以实现这一功能。

  关于Win32的窗口子类化就介绍到这里了,你可以 点这里下载本文的配套源代码 ,代码中有详细的注释。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值