Visual C++实现定制标题栏
要想用VC編寫出一個漂亮的界面通常是比較費時的,主要原因有兩個,一是VC中自帶的組件少而不亮,二是無法進行可視化設計。不過,這些都只是針對常用的方法罷了,如果你采用子類化,鉤子等技術,可以完全改變這處局面,甚至比Delphi等RAD工具還要快.本文中就將介紹如何編寫一個DLL,如何來輕松的定制標題欄。
為了使每個窗體的標題欄都能定制,并且不用為每一個窗體類編碼,所以本方法采用鉤子技術,其核心思想是監控 Windows 消息,處理需要重緩標題的消息,以達到定制標題欄的思想。
本文件介紹的方法將在應用程序中安裝 WH_CALLWNDPROC 鉤子,具體的代碼如下所示:
在應用程序啟動時安裝鉤子的代碼:
extern "C" BOOL __declspec(dllexport) InstallCallWndHook()
{
g_hCallWndProc = SetWindowsHookEx(WH_CALLWNDPROC, CallWndProc, AfxGetInstanceHandle(),GetCurrentThreadId());
if (NULL == g_hCallWndProc)
return FALSE;
else
return TRUE;
}
InstallCallWndHook() 函數定義為出口函數,在需要定制標題的程序中將調用它,由于安裝的是 WH_CALLWNDPROC 鉤子,所以在應用程序調用自己的窗體過程之前,總會先調用 CallWndProc;如果設置為 WH_CALLWNDPROCRET 則順序剛好相反
g_hCallWndProc為一內存共享變量,其它定義的方法如下所示:
#pragma data_seg("Shared")
static HHOOK g_hCallWndProc;
#pragma data_seg()
當然你也可以定義為其它形式,比如直接采用共享內存API創建方式。
在應用程序退出時安裝卸載鉤子的代碼:
extern "C" void __declspec(dllexport) UnInstallCallWndHook()
{
if (g_hCallWndProc != NULL)
{
UnhookWindowsHookEx(g_hCallWndProc);
g_hCallWndProc = NULL;
}
}
定制標題欄的入口函數為 CallWndProc(),其代碼如下:
LRESULT __declspec(dllexport) CALLBACK CallWndProc(
int code, // hook code
WPARAM wParam, // undefined
LPARAM lParam // address of structure with message data (CWPSTRUCT)
)
{
DWORD dwThreadID = (DWORD)wParam;
LPCWPSTRUCT pCwpStruct = LPCWPSTRUCT(lParam);
if (HC_ACTION == code)
{
if ((pCwpStruct->message == WM_MOUSEMOVE)
|| (pCwpStruct->message == WM_SETCURSOR)
|| (pCwpStruct->message == WM_NCHITTEST)
|| (pCwpStruct->message == WM_KICKIDLE)
|| (pCwpStruct->message == WM_NCMOUSEMOVE)
|| (pCwpStruct->message == WM_MOUSEACTIVATE)
|| (pCwpStruct->message > WM_USER));
else
DrawFrame(pCwpStruct);
}
return CallNextHookEx(g_hCallWndProc, code, wParam, lParam);
}
上面代碼中的 if 語句主要用來判斷收到哪些消息時需要重繪標題欄,有興趣的朋友可以對這段代碼進行改進。
在函數 DrawFrame 中將實現對窗體標題欄和邊框的繪制,標題的繪制有兩種方法,一是直接畫圖,二是貼圖的方式。在本文中將實現兩種方法,如果在當前目錄下有 active.bmp 和 inactive.bmp 兩個文件,則采用它們所代表的位圖作為窗體的標題欄,否則采用畫圖的方式。
由于只繪制標題欄,所以需要對 CallWndProc 進行過濾,對于非窗體如 Button 則不進行繪制,本文中僅以如下簡單的方法來處理:
char szClassName[128] = {0};
::GetClassName(pCwpStruct->hwnd, szClassName, sizeof(szClassName));
if (strcmp(szClassName, "#32770") != 0)
return ;
實際中不能這樣中,因為很多窗體的類名可能不是"#32770",比較好的方法建議去判斷 pCwpStruct->hwnd 所代表的對象是否有父窗體,調用 GetParent 判斷一下即可。
在正式繪制之前還必須判斷窗體是處于活動狀態還是非活動狀態,這樣就可以區分在兩種不同的狀態下繪制不同的標題欄和邊框了。
下面這段代碼就是用來繪制標題欄的:
if (bActive)
hBitmap = (HBITMAP)::LoadImage(NULL, _T("active.bmp"), IMAGE_BITMAP, nWidth, nHeight, LR_LOADFROMFILE);
else
hBitmap = (HBITMAP)::LoadImage(NULL, _T("inactive.bmp"), IMAGE_BITMAP, nWidth, nHeight, LR_LOADFROMFILE);
if (NULL == hBitmap)
{
DrawTitleBar(dcWin, rcNcClient, 0);
}
else
{
dcMem = ::CreateCompatibleDC(dcWin);
hOldBitmap = (HBITMAP)::SelectObject(dcMem, hBitmap);
::StretchBlt(dcWin,0,0,nWidth, nHeight,dcMem,0,0,nWidth, nHeight,SRCCOPY);
::SelectObject(dcMem, hOldBitmap);
::DeleteDC(dcMem);
}
其中變量bActive為TRUE時表示窗體處于活動狀態,為FALSE時表示窗體處于非活動狀態。兩個 LoadImage 函數分別用來將兩種狀態下的位圖裝載到內存中,以便下一步進行貼圖.當 LoadImage 不成功時,表示當前目錄下沒有 active.bmp 和 inactive.bmp 文件中或文件格式不正確,在這處情況下就調用 DrawTitleBar 函數對標題欄進行繪畫。繪畫的方法可以隨便,但要繪在矩形 rcNcClient 內,因為這個矩形就是標題欄所在區域。
如果 LoadImage 成功,則直接將位圖貼到標題欄中。接下來就是繪制邊框了,在繪制之前還需要計算出邊框的所在矩形,然后再在dcWin上按要求進行繪制即可。完整的示例源代碼請到 http://www.megspace.com/computers/bigtime/custtitlebar.htm 上下載(注意:由于申請的是國外免費空間,所以需要將瀏覽器的編碼改為“簡體中文”),解壓后運行即可看到示例的所述界面
這種方法的關鍵地方是安裝合適的鉤子,然后對合適的消息進行處理,采用這種方法可以改變幾乎任何一可見窗體的外觀,包括其它程序的窗體等,對于特殊的窗體等只需要進行專門處理即可。
由于編譯成的是 DLL 文件中,所以可以很輕松的運用到其它程序中。只需要在需要用到的程序中調用 InstallCallWndHook 安裝這個鉤子即可。
總結
以上是生活随笔為你收集整理的Visual C++实现定制标题栏的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python加粗_python – 设
- 下一篇: python递归面试题_python面试