本帖最后由 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
|