[教程]第七讲 GDI自绘一个控件
ACN论坛.讲师:seniors 转载请说明此文出处所在:<<简易工作室>>,谢谢!
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```
本文固定链接: http://jianyiit.com/post-43.html
发表评论
木有头像就木JJ啦!还木有头像吗?点这里申请属于你的个性Gravatar头像吧!