seniors 发表于 2013-5-18 12:30:23

第七讲 GDI自绘一个控件

本帖最后由 seniors 于 2013-6-16 07:57 编辑

GDI 之自绘一个控件
一、控件自绘,实际就是让自己来绘制控件,接管系统的绘制功能
有的控件绘制是用WM_DRAWITEM,有的是用WM_PAINT,有的用WM_NCPAINT
WM_NCPAINT窗口非客户区(框架)的自绘最复杂,因为框架上有三个系统按钮、窗口菜单有的找不到系统是在哪里绘制的
WM_DRAWITEM其实最终也应该是在WM_PAINT中绘制
WM_PAINT窗口客户区绘制,这一讲就用接管WM_PAINT来绘制一个链接地址控件
二、接管WM_PAINT
GUIRegisterMsg能接管到控件的WM_DRAWITEM,但接收不到控件的WM_PAINT
所以我们要用_WinAPI_SetWindowLong($hWnd, $GWL_WNDPROC, $ptrCallback),来设置窗口的自绘
$hWnd, 控件的句柄
$GWL_WNDPROC,说明是设置控件的窗口处理
$ptrCallback,指针指向处理窗口的函数

为此,我们要注册一个回调函数,使用
$hCallback = DllCallbackRegister("YourFunc", "int", "hWnd;uint;wparam;lparam")
再取得回调函数的指针
$ptrCallback = DllCallbackGetPtr($hCallback)

这三句话结合起来,就是说$hWnd这个控件的处理,我让"YourFunc"来处理
我们只需要在YourFunc中处理WM_PAINT消息就行了
在WM_PAINT消息处理中
我们要用_WinAPI_BeginPaint和_WinAPI_EndPaint获得DC和释放DC
这两个函数能可以删除消息队列中的WM_PAINT消息,并使无效区域有效。
_WinAPI_GetDC和_WinAPI_ReleaseDC并不删除WM_PAINT消息也不能使无效区域有效,因此当程序跳出WM_PAINT时,无效区域仍然存在。系统就会不断
发送WM_PAINT消息,于是程序不断处理WM_PAINT消息。

三、其它控制
链接地址控件还需要在鼠标hover时,显示下划线,所以还要处理鼠标移动的消息
同样只要在YourFunc中接管就行了
为了要记住鼠标有没有覆盖,所以我们加了一个全局变量$bHover
这样就带来一个问题,如果我的界面中有两个链接地址控件,那怎么办呢?

大家思考一下,这一讲界面上看就一行文字,其实处理过程中用了很多东西,大家慢慢体会。
下一讲,再讲多个同种控件的自绘

#include <APIConstants.au3>
#include <WinAPIEx.au3>
Global $bHover = False
;注册回调函数
Global $hCallback = DllCallbackRegister("YourFunc", "int", "hWnd;uint;wparam;lparam");函数名,返回值,参数
Global $ptrCallback = DllCallbackGetPtr($hCallback)

GUICreate("第七讲", 300, 200)
Global $nlableId = GUICtrlCreateLabel("我是一个链接地址:", 10, 10)
Global $hlableWnd = GUICtrlGetHandle($nlableId)

;设置lable控件的处理函数,也就是所谓的控件子类化
Global $hOldProc = _WinAPI_SetWindowLong($hlableWnd, $GWL_WNDPROC, $ptrCallback)
GUISetState()

While 1
        $Msg = GUIGetMsg()
        Switch $Msg
                Case -3
                        ExitLoop
                Case $nlableId
                        ShellExecute("www.autoitx.com")
        EndSwitch
WEnd
GUIDelete()
Exit

Func YourFunc($hWnd, $iMsg, $wParam, $lParam)
        Switch $iMsg
                Case $WM_PAINT
                        Local $tPAINTSTRUCT;接收_WinAPI_BeginPaint返回的$tagPAINTSTRUCT结构,这结构内部参数我还不清晰
                        ;获取控件DC并消除WM_PAINT消息,这函数一定要用_WinAPI_EndPaint($hWnd, $tPAINTSTRUCT)解除
                        Local $hDC = _WinAPI_BeginPaint($hWnd, $tPAINTSTRUCT)
                        ;获取控件长高
                        Local $HWND_CX = _WinAPI_GetWindowWidth($hWnd)
                        Local $HWND_CY = _WinAPI_GetWindowHeight($hWnd)
                        Local $tRect = _WinAPI_CreateRect(0, 0, $HWND_CX, $HWND_CY);控件自绘,坐标总是控件窗口的坐标,和父窗口无关
;~                         _WinAPI_FillRect($hDC, DllStructGetPtr($tRect), _WinAPI_GetSysColorBrush($COLOR_3DFACE));用系统背景画刷清空背景
                       
                        Local $sText = _WinAPI_GetWindowText($hWnd);获取窗口标题,就是lable上的文字
                        _WinAPI_SetTextColor($hDC, 0xFF0000);设置文字颜色
                        _WinAPI_SetBkMode($hDC, $TRANSPARENT);设置背景透明
                        ;获得系统默认字体
                        Local $hFont = _WinAPI_GetStockObject($DEFAULT_GUI_FONT)
                        If $bHover Then;如果鼠标覆盖的话,设置下划线字体
                                Local $tLOGFONT = DllStructCreate($tagLOGFONT)
                                Local $iSizeLOGFONT = DllStructGetSize($tLOGFONT)
                                Local $pLOGFONT = DllStructGetPtr($tLOGFONT)
                                _WinAPI_GetObject($hFont, $iSizeLOGFONT, $pLOGFONT);获取字体结构
                                DllStructSetData($tLOGFONT, "Underline", True);设置字体加下划线,False是没有下划线
                                $hUnderLineFont = _WinAPI_CreateFontIndirect($tLOGFONT)
                                _WinAPI_SelectObject($hDC, $hUnderLineFont)
                                _WinAPI_DrawText($hDC, $sText, $tRect, BitOR($DT_LEFT, $DT_SINGLELINE))
                                _WinAPI_DeleteObject($hUnderLineFont)
                        Else
                                _WinAPI_SelectObject($hDC, $hFont)
                                _WinAPI_DrawText($hDC, $sText, $tRect, BitOR($DT_LEFT, $DT_SINGLELINE))
                        EndIf
                        Return _WinAPI_EndPaint($hWnd, $tPAINTSTRUCT);返回,一定要用_WinAPI_EndPaint($hWnd, $tPAINTSTRUCT) 结束WM_PAINT
                Case $WM_MOUSEMOVE;鼠标覆盖
                        If Not $bHover Then;如果本来没有覆盖就设置覆盖标志,并重绘控件窗口
                                $bHover = True
                                _WinAPI_InvalidateRect($hWnd, 0, False);False是不擦除背景,因为加了一个下划线,本来的图案不影响我们
                        EndIf
                        _WinAPI_TrackMouseEvent($hWnd, 0x00000002);0x00000002是侦测鼠标离开客户区事件,如果离开则发送$WM_MOUSELEAVE消息,不加这一句是永远没有$WM_MOUSELEAVE消息的
                        ;TME_CANCEL 0x80000000
                        ;TME_HOVER 0x00000001
                        ;TME_LEAVE 0x00000002
                        ;TME_NONCLIENT 0x00000010
                        ;TME_QUERY 0x40000000
                Case $WM_MOUSELEAVE;鼠标离开
                        $bHover = False;离开则置覆盖标志为否
                        _WinAPI_InvalidateRect($hWnd, 0, True);重绘控件窗口,True为擦除背景,因为本来已经绘制了下划线,如果不擦除的话,则只写文字是遮挡不信下划线的,也就是一直能看到下划线
                        ;WM_PAINT收到重绘消息后,看到擦除背景为true,会先发出$WM_ERASEBKGND消息,再重绘
                        ;你也可以用false,那就要在WM_PAINT里清空背景,也就是WM_PAINT里注释掉的那句,这样下面的$WM_ERASEBKGND就不要了,我这里是演示用$WM_ERASEBKGND来擦除背景
                        ;你甚至可以在$WM_ERASEBKGND里画各种好看的背景
                Case $WM_ERASEBKGND
                        Local $hDC = $wParam
                        Local $HWND_CX = _WinAPI_GetWindowWidth($hWnd)
                        Local $HWND_CY = _WinAPI_GetWindowHeight($hWnd)
                        Local $tRect = _WinAPI_CreateRect(0, 0, $HWND_CX, $HWND_CY);控件自绘,坐标总是控件窗口的坐标,和父窗口无关
                        _WinAPI_FillRect($hDC, DllStructGetPtr($tRect), _WinAPI_GetSysColorBrush($COLOR_3DFACE));用系统背景画刷清空背景
                        Return 1
        EndSwitch
        Return _WinAPI_CallWindowProc($hOldProc, $hWnd, $iMsg, $wParam, $lParam);没有处理的消息让原先的处理程序处理
EndFunc   ;==>YourFunc

Qokelate 发表于 2013-5-18 12:37:19

沙发,前排留名,此贴必火 楼主辛苦了   顺便广告位招租~~~~~~~~

haijie1223 发表于 2013-5-18 13:13:42

这一讲没怎么看明白~

seniors 发表于 2013-5-18 13:24:03

回复 3# haijie1223
讲一下,哪一段没明了

xms77 发表于 2013-5-18 13:24:42

每天一讲,楼主辛苦~~

haijie1223 发表于 2013-5-18 13:30:33

回复 4# seniors


    我一直以为自绘就是代替GUICtrlCreateLabel这样的操作,看来好像不是~

seniors 发表于 2013-5-18 13:44:42

回复 6# haijie1223
下一讲就要代替了,
代替的意思其实就是UDF化了,可以通用
这一讲的并不是通用的,问题就在哪个$bHover变量上,一个控件一个变量,那程序没法编

pccp 发表于 2013-7-27 10:45:22

继续收藏,不能囫囵吞枣。
页: [1]
查看完整版本: 第七讲 GDI自绘一个控件