编写 Windows 标准控件 小结
   作者:AoGO 于2008-1-17上传 

    在前面两章,我们编写了两个标准行为的控件,这一章,我们来回顾一下这两章的重点,以及我的一些遗漏。在本章,所有涉及控件的含义,仅仅表示标准控件,自定义的控件不在讨论范围之内。

    1.什么时候需要自己写控件

    写控件并不是一件很轻松的事,复杂的控件尤其如此,在决定写一个控件之前,考虑一下是否有更好的办法,比如,觉得按钮的 3D 边框难看,想有平坦风格,只因为这个原因而自己写一个新控件,这是得不尝失的,代码不多,但也不少,以后的扩展性,维护性,如果是团队开发,如果让使用者最快上手的使用文档,等等,像上述的控件,完全可以超类化原有的按钮控件,简单地接管几个消息即可达到同样的目的。还有,控件之间的组合,同样可以扩展为新的控件,比如,ListView 控件中的列标题其实是一个单独的控件,并不需要在ListView中单独再实现列标题控件的功能,这种控件组合是很方便的。

   2.写之前的设计

    要写控件,首先要做的事,就是设计控件的文档,比如,第一章与第二章中的FlatButton及MyList控件,代码我一个字都没打,文档已经定义好了,首先,是控件名字,假设是 QuickHelper ,那取单词的首字符,来做为我们定义的前缀。

    比如消息是 QHM_ ,窗口风格是 QHS_ ,通知消息是 QHN_ ,等等。

    接下来就是各细节的定义,这个并不一定要局限于一套行为,按自己的想法固定就可以了,但是一定要注意就是不要散乱,一直使用自己的固定定义格式,形成自己的风格,这样,当写新的控件时,建立一个文档是很快速的。而不需要每次都去想这个那个要放哪。我在这里说一下我的习惯,可以参考一下,因为我是按标准的 Windows 控件的方式定义文档的(再次重申并不一定要这样做)。

    首先是窗口风格,所有的控件可以使用32个风格位,高16位是系统使用的,也就是所有的 WS_ 开头的风格,低16位可以自由使用,这16个系统给你的变量,应该如何使用最好呢?标准的行为是优先用来控制显示功能,如,几乎所有的 Windows 标准控件(系统自带的)都是通过更改风格来设置不同的显示,Button 可以更改成复选框,单选框,文本对齐,图形,列表框通过风格来选择是下拉/列表/下拉列表,等等,这样在对话框设计时,就可以直接看到效果。

    如果还有余,而不需要用来预留给以后扩展,那其次是用来控制控件与用户的交互行为,比如是否需要按键,是否需要自动对齐等等用户简单控制地功能。

    如果还有余,那就自由发挥了,一般的做法,是预留,方便以后扩展其它外观显示功能。

    如果不够,那我们就只能在窗口的结构内存中保存了,但一个控件一般16个位是完全够了的,因为还要考虑到位的组合,比如,文本对齐:

    QHS_CENTER equ 1h
    QHS_RIGHT equ 2h
    QHS_LEFT equ QHS_CENTER or QHS_RIGHT

    这样,三个风格只使用了二个位,在获得窗口风格后这样:

    GetWindowLong,hWnd,GW_STYLE
    and eax,QHS_LEFT
    .if eax == QHS_LEFT
...
    .elseif eax & QHS_CENTER
...
    .else
...
    .endif

    通过位的组合,可以把16个扩展为20个甚至30个,当然,前提是,这些位是同一个功能的不同控制,没有冲突,比如像 Button 控件,通过几个大的风格,扩展其它风格时使用同样的值,因为它们是不会冲突的:

    BS_PUSHBUTTON   equ 0h
BS_DEFBUTTON         equ 10h

    BS_RADIOBOX     equ 1h
BS_ABC                equ 10h

    BS_CHECKBOX     equ 2h
        BS_AUTOCHECKED        equ 10h

    全局控制位:

    BS_CHECKBOX     equ 8000h

    看上面的信息,就会知道,0/1/2这几个是大的风格变化,而equ 10的,是不同的风格,但是只针对各自的大风格才有用,在其它风格中是无另外的控制,就可以通过这样来实现拥有更多的控制位,而全局控制位则适用于所有不同类型的风格。

    接下来我们看通知码 QHN_ ,WM_COMMAND 这个通知消息,它只能进行最简单地通知,使用者只能收到消息,而无法按管,我们这里就不再详细讨论。WM_NOTIFY 则不同,它能够传递随意多的信息,这个没有什么具体的限制与要求,结构可以使用系统 NMHDR,也可以使用自己的,但是前提是前面必须包含 NMHDR 结构,这是系统通知消息格式,不可更改,其它的就没什么要求,应该通知什么,如何处理通知,是否根据返回值决定哪位操作,这是控件的功能来决定的。

    接下来就是消息了,自己的消息必须大于 WM_USE,小于 WM_USER 是系统消息,控件消息是控件自身的功能,我们无法针对性讨论,谈谈标准消息,一般而言,小部分消息是所有控件都要使用到的,比如WM_PAINT,WM_DESTORY等等,如果需要鼠标,那WM_LBUTTONDOWN/WM_LBUTTONUP等等,键盘WM_KEYDOWN/WM_KEYUP,风格更改时WM_CAPTURECHANGED,这些消息都是系统消息,我们只管处理。我这里着重要讲的是可以按管的消息,举个列子:

    WM_SETFONT     设置字体,事实上,这个消息仅仅是通知而已,标准文档是:

    wParam==hFont         ;字体句柄

    lParam==bDraw         ;是否立即重绘

    返回前一次的字体

    那么,根据这个定义,你按管这个消息,所wParam指向的字体句柄保存到控件结构中,然后根据lParam来是否刷新,然后返回前一次的控件句柄即可。

    类似的还是WM_GETDLGCODE,WM_CAPTURECHANGING,WM_SIZING等等。。。其中WM_SETTEXT也可以完全接管,这里就不一一详述了。

    消息的定义,也尽量化繁为简,比如,第二章的MyList控件,就有消息MLM_ADDSTRING与MLM_SETITEM,MLM_SETITEM 对于添加一个基本项目来说过于麻烦,则多定义一个消息MLM_ADDSTRING来实现快速添加,当用户添加之后,他就会考虑,为什么有图标呢,就会去翻文档找到WM_SETITEM,则引起兴趣。如果没有MLM_ADDSTRING,一个新用户要多费上几分钟的时间去尝试,考虑一下每次都要添加,他自己就会去写个类似MLM_ADDSTRING的函数了。

    同时,消息定义要完善好文档,wParam是什么,lParam是什么,返回值是什么,要清楚,消息地这些格式有变化,要同步修改文档,否则过上几天,自己用都要再参考代码。

    另外一个比较重要的,是界面重绘通知消息,这个也没有定性,简单地,可以使用WM_DRAWITEM来实现,但是这个消息标准行为是完全性按管,也就是父级重绘,有些时候我们的控件界面显示很复杂,分为多个不同单元,每个单元都想通知并由父级处理,那好的方法,是使用自己的消息,定义wParam与lParam,然后传一个指针,标识重绘区域与目标信息,再根据返回值,适当地完成接下来的工作,这种方式不是很常用,只有像电子表格这种复杂的控件,或者组合控件才有可能会用到,比如一个组合控件,把子控件的重绘通过消息再次发给自己的父窗口,由它去处理。

    另外一个,我发现一个奇怪的现象,可能是受罗云彬的影响,大部分人的窗口回调中,喜欢在ret之前 加上xor eax,eax,而在消息中返回值,如:

    xxx proc ... ...
.if uMsg == *
    mov eax,true
            ret
.else
    invoke DefWindowProc....
.endif
xor eax,eax
        ret
    xxx endp

    这是不可取的,正确的方法应该是直接 ret,而在消息里面消息地处理回调函数:

    xxx proc ... ...
.if uMsg == *
    ...
    mov eax,TRUE
.else
    invoke DefWindowProc....
.endif
        ret
    xxx endp

    理由我就不多说了,如果把这习惯带到写控件的程序里,你会想杀人的。。

    3 无闪烁刷新

    前面二章的控件,都是简单重绘的控件,不需要也没必要搞无闪烁刷新,只有像同时显示大量文本/图形的控件,才需要考虑这个,其实重点就是,不要背景重绘,WM_ERASEBKGND直接返回TRUE,或注册控件时设置空画刷背景。在 WM_PAINT 中,画背景时也要分开,把各个可以分开的部分使用不同的函数来画,在其它消息中。不要去InvalidateRect,而是获得需要重绘的部分,直接调用对应的函数去画。同时,如果有滚动条,则使用ScrollWindow来滚动,然后重绘滚动后的空白部分,这其实就是无闪烁刷新的关键。

 

    小结就到这里,还是那句话,学编程就像追MM,看着有感觉,就动手干吧,不要错过机会。人都有低潮的时候,在有感觉的时候不动手学,就浪费这大好光阴了。

    最后,过年了,祝大家新年快乐,事业进步!



<<<上一篇
欢迎访问AoGo汇编小站:http://www.aogosoft.com
下一篇>>>