?? skindialog.cpp
字號:
#include "stdafx.h"
#include "SkinDialog.h"
#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif
static HHOOK hSkinDialogHook=NULL;
CSkinDialog *g_pGlobalSkinDialogObj=NULL;
//------------------------------------------------------------------------
// 枚舉子窗口所用的回調函數,用來讓CSkinButtonSubClass當中的所有Button
// 順便看看有沒有Icon類型的Static,如果有,那么把它和背景融合一下.
BOOL CALLBACK EnumSkinWindowProc(HWND hwnd,LPARAM lParam)
{
TCHAR szBuffer[512]={NULL};
CSkinButton *pSkinButton=(CSkinButton*)lParam;
GetClassName(hwnd,szBuffer,512);
ASSERT(g_pGlobalSkinDialogObj != NULL); // 必須確保全局的CSkinDialog被賦值.
if (lstrcmpi(szBuffer,"Button") == 0)
{
DWORD dwOldStyle=GetWindowLong(hwnd,GWL_STYLE);
if (((dwOldStyle & BS_RADIOBUTTON) != BS_RADIOBUTTON) && ((dwOldStyle & BS_AUTORADIOBUTTON) != BS_AUTORADIOBUTTON)
&& ((dwOldStyle & BS_CHECKBOX) != BS_CHECKBOX))
{
if (GetProp(hwnd,"SkinButtonHandled") == NULL)
{
dwOldStyle |= BS_OWNERDRAW; // 增加BS_OWNERDRAW屬性以便獲得WM_DRAWITEM消息.
SetWindowLong(hwnd,GWL_STYLE,dwOldStyle);
pSkinButton->SubClassWindow(hwnd); // CSkinButton 類只對真正的按鈕感興趣.
}
}
}
else if (lstrcmpi(szBuffer,"Static") == 0)
{
ICONINFO IconInfo={NULL};
HICON hIcon=(HICON)SendMessage(hwnd,STM_GETICON,0,0);
if (hIcon != NULL)
{
// 增加SS_BITMAP標記,同時去掉SS_ICON標記。
DWORD dwOldStyle=GetWindowLong(hwnd,GWL_STYLE);
dwOldStyle &= ~SS_ICON;
dwOldStyle |= SS_BITMAP;
SetWindowLong(hwnd,GWL_STYLE,dwOldStyle);
// 由于圖標的顏色位數和顏色表的關系,漸進色的背景無法正確的和Icon進行融合。
// (通過構造刷子實現的結果是背景色被強制轉換成最接近的顏色)
//
// 這里采用的方法是把Icon轉換成增加了背景后的Bitmap然后顯示。
//
// 好麻煩的東西。:)
CRect rect;
CBitmap Bitmap;
CDC BitmapDC;
CDC ClientDC;
CBitmap ClientBitmap;
CDC PrevDC;
CRect rect2;
CBrush Brush;
HWND hwndParent=GetParent(hwnd); // 因為這是在EuumChildWindows中調用的,所以hwnd一定是一個子窗口的hWnd
CBitmap *pOldClientBitmap=NULL;
CBitmap *pOldBitmap=NULL;
PrevDC.Attach(GetDC(hwnd));
GetClientRect(GetParent(hwnd),&rect);
GetWindowRect(hwnd,&rect2);
::ScreenToClient(GetParent(hwnd), (LPPOINT)&rect2);
::ScreenToClient(GetParent(hwnd), ((LPPOINT)&rect2)+1);
ClientDC.CreateCompatibleDC(&PrevDC);
BitmapDC.CreateCompatibleDC(&PrevDC);
ClientBitmap.CreateCompatibleBitmap(&PrevDC,rect.Width(),rect.Height()); // 整個窗體背景的那個Bitmap
Bitmap.CreateCompatibleBitmap(&PrevDC,rect2.Width(),rect2.Height()); // 用來畫圖標的那個小的Bitmap
pOldClientBitmap=ClientDC.SelectObject(&ClientBitmap); // 選中對象
pOldBitmap=BitmapDC.SelectObject(&Bitmap);
g_pGlobalSkinDialogObj->DoGradientFill(&ClientDC,rect); // 填充漸進色背景
// 然后從背景中把這個空間所在的位置對應的圖象“摳”出來
BitmapDC.BitBlt(0,0,rect2.Width(),rect2.Height(),&ClientDC,rect2.left,rect2.top,SRCCOPY);
// 畫原來的圖標
VERIFY(DrawIcon(BitmapDC,0,0,hIcon));
// 更新控件的圖象為位圖
SendMessage(hwnd,STM_SETIMAGE,IMAGE_BITMAP,(LPARAM)(HBITMAP)Bitmap);
ClientDC.SelectObject(pOldClientBitmap);
BitmapDC.SelectObject(pOldBitmap);
PrevDC.DeleteDC();
Bitmap.Detach(); // 如果讓析構函數銷毀這個位圖的話,我們就什么也得不到了。
}
}
// 繼續枚舉當前窗口的子窗口,把按鈕類型的Style增加上OwnerDraw標記。
// 通常都是沒什么用的,因為很多子窗口還沒開始構造出來呢. ^_^
EnumChildWindows(hwnd,EnumSkinWindowProc,lParam);
return TRUE;
}
//----------------------------------------------------------------------------------
// CSkinDialog Subclass后的WndProc過程
LRESULT CALLBACK SkinWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
WNDPROC OldWndProc=NULL;
OldWndProc=(WNDPROC)GetProp(hwnd,"SkinWindowOldWndProc");
ASSERT(OldWndProc != NULL);
if (uMsg == WM_DRAWITEM)
{
// 將收到的WM_DRAWITEM發送到對應的控件中。做到真正的“屬主畫”->“自畫”
LPDRAWITEMSTRUCT pDrawInfo=(LPDRAWITEMSTRUCT)lParam;
if ((pDrawInfo->CtlType = ODT_BUTTON))
{
// 如果這個按鈕被CSkinButton Subclass或這個按鈕是由CSkinButton控制的話,我們才會把消息轉發.
// 否則在打開公用對話框中會被重復調用的.
// 默認情況下,WM_DRAWITEM只會被發送給窗體,而不是對應的控件.
if ((GetProp(pDrawInfo->hwndItem,"SkinButtonObj") != NULL) // 這是通過CSkinButton subclass后的按鈕.
|| (GetProp(pDrawInfo->hwndItem,"SkinButtonHandled") != NULL)) // 這是使用MFC方法調用的CSkinButton
{
return SendMessage(pDrawInfo->hwndItem,uMsg,wParam,lParam);
}
}
else
{
return CallWindowProc(OldWndProc,hwnd,uMsg,wParam,lParam);
}
}
else if (uMsg == WM_DESTROY)
{
SetWindowLong(hwnd,GWL_WNDPROC,(LONG)OldWndProc);
RemoveProp(hwnd,"SkinWindowOldWndProc");
return CallWindowProc(OldWndProc,hwnd,uMsg,wParam,lParam);
}
else if (uMsg == WM_ERASEBKGND)
{
LONG nWindowStyle=GetWindowLong(hwnd,GWL_STYLE);
if ((((nWindowStyle & WS_DLGFRAME) == WS_DLGFRAME) // 只針對對話框類型的窗體或者標記為使用CSKinDialog的
|| (GetProp(hwnd,"SkinWindowHandled") != NULL))
&& ((nWindowStyle & WS_MINIMIZEBOX) == 0))
{ // 否則,在使用BCG的時候會把彈出的菜單也繪制上的.
TCHAR szBuffer[128];
GetWindowText(hwnd,szBuffer,128);
TRACE("SkinDialog receive WM_ERASEBKGND message for window %s.\n",szBuffer);
CDC dc;
CRect rect;
dc.Attach((HDC)wParam);
::GetClientRect(hwnd,&rect);
// 我們自己畫一遍
g_pGlobalSkinDialogObj->DoGradientFill(&dc,rect);
dc.Detach();
// 不交給以前的代碼畫,默認的MFC的代碼會把我們的漸變背景擦掉的。
// CallWindowProc(OldWndProc,hwnd,uMsg,wParam,lParam);
// 我們已經自己繪制了,不勞系統費心了 :)。
return TRUE;
}
}
else if (uMsg == WM_CTLCOLORSTATIC)
{
TCHAR szBuffer[512]={NULL};
GetClassName((HWND)lParam,szBuffer,512);
if (lstrcmpi(szBuffer,"Edit") == 0) return (long)(HBRUSH)GetStockObject(WHITE_BRUSH);
GetWindowText((HWND)lParam,szBuffer,512);
if (lstrcmpi(szBuffer,"") != 0) // 對于標題不為空的Static,我們把背景去掉.
{ // TODO: 對于Frame,這樣會把后面的線條漏出來,下一版想辦法解決.
if ((g_pGlobalSkinDialogObj == NULL)
|| ((g_pGlobalSkinDialogObj->m_dwFlags & SKINDIALOG_FLAGS_FORCE_STATIC_BACKGROUND_BRUSH) != SKINDIALOG_FLAGS_FORCE_STATIC_BACKGROUND_BRUSH))
{
SetBkMode((HDC)wParam,TRANSPARENT);
HBRUSH hbrush=CreateSolidBrush(RGB(100,100,255));
return (long)hbrush;
}
else
{
// 原來處理圖標背景的方法,創建一個特殊的刷子來繪制背景,但最后發現不行,顏色數的問題。
// 沒想到的是,居然可以用這種方法對付那些"頑固"的靜態標簽,效果當然還是差一點了.
// 考慮一下要不要也換成位圖.
CDC DC;
CRect rect;
CBitmap Bitmap;
CDC BitmapDC;
CDC ClientDC;
CBitmap ClientBitmap;
CDC PrevDC;
CRect rect2;
CBrush Brush;
HBRUSH hbr;
CBitmap *pOldClientBitmap=NULL;
CBitmap *pOldBitmap=NULL;
PrevDC.Attach(GetDC(hwnd));
DC.Attach((HDC)wParam);
DC.SetBkMode(TRANSPARENT);
GetClientRect(hwnd,&rect);
GetWindowRect((HWND)lParam,&rect2);
::ScreenToClient(hwnd, (LPPOINT)&rect2);
::ScreenToClient(hwnd, ((LPPOINT)&rect2)+1);
ClientDC.CreateCompatibleDC(&PrevDC);
BitmapDC.CreateCompatibleDC(&PrevDC);
ClientBitmap.CreateCompatibleBitmap(&PrevDC,rect.Width(),rect.Height());
Bitmap.CreateCompatibleBitmap(&PrevDC,rect2.Width(),rect2.Height());
pOldClientBitmap=ClientDC.SelectObject(&ClientBitmap);
pOldBitmap=BitmapDC.SelectObject(&Bitmap);
g_pGlobalSkinDialogObj->DoGradientFill(&ClientDC,rect);
BitmapDC.BitBlt(0,0,rect2.Width(),rect2.Height(),&ClientDC,rect2.left,rect2.top,SRCCOPY); // Copy the content to the Icon DC.
// NOTE: 由于有些圖標是16色的,而背景是32位真彩色的,所以有的圖標背景會出現不完全符合的情況。
// TODO: 通過轉換圖標到32位真彩色來解決這個問題. -- FAILED 沒有找到對應的轉換函數. :(
Brush.CreatePatternBrush(&Bitmap);
ClientDC.SelectObject(pOldClientBitmap);
BitmapDC.SelectObject(pOldBitmap);
PrevDC.DeleteDC(); // 這個是我們通過GetDC生成的一個新的,我們必須負責銷毀。
DC.Detach(); // 這個DC不是我們構造的,所以我們不能銷毀它。
hbr=Brush;
Brush.Detach();
return (long)(HBRUSH)hbr;
}
}
}
return CallWindowProc(OldWndProc,hwnd,uMsg,wParam,lParam);
}
//----------------------------------------------------------------------------
// Hook 所用的回調函數
LRESULT CALLBACK CBTProc(int nCode, WPARAM wParam, LPARAM lParam)
{
if(nCode < 0)
{
return CallNextHookEx(hSkinDialogHook, nCode, wParam, lParam);
}
switch(nCode)
{
case HCBT_ACTIVATE:
HWND hwnd = (HWND)wParam;
CSkinDialog SkinDialog;
SkinDialog.SubClassDialog(hwnd); // 讓SubClassDialog去判斷是不是重復Hook了.
return 0;
}
// 調用下一個Hook
return CallNextHookEx(hSkinDialogHook, nCode, wParam, lParam);
}
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
CSkinDialog::CSkinDialog()
{
}
CSkinDialog::~CSkinDialog()
{
}
BOOL CSkinDialog::SubClassDialog(HWND hWnd)
{
if (g_pGlobalSkinDialogObj == NULL)
{
g_pGlobalSkinDialogObj=this;
}
if (hSkinDialogHook == NULL)
{
// 沒有安裝過Hook?我們安裝它。
hSkinDialogHook = SetWindowsHookEx(
WH_CBT,
CBTProc,
NULL,
GetCurrentThreadId() // 只Hook當前線程!!!
);
}
// 首先,枚舉所有的子窗口,把按鈕類型的Style增加上OwnerDraw標記。
EnumChildWindows(hWnd,EnumSkinWindowProc,(LPARAM)&m_bSkinButtonsTemplate);
if (GetProp(hWnd,"SkinWindowOldWndProc") == NULL)
{
// 獲取以前的WndProc
WNDPROC OldWndProc=(WNDPROC)GetWindowLong(hWnd,GWL_WNDPROC);
// 保存下來。
SetProp(hWnd,"SkinWindowOldWndProc",(HANDLE)OldWndProc);
// 設置新的WndProc
SetWindowLong(hWnd,GWL_WNDPROC,(LONG)SkinWindowProc);
SetProp(hWnd,"SkinWindowHandled",(HANDLE)1L);
}
else
{
// 只允許SubClass一次。
return FALSE;
}
return FALSE;
}
BOOL CSkinDialog::RemoveSubClass()
{
BOOL bResult=FALSE;
if (hSkinDialogHook != NULL)
{
bResult=UnhookWindowsHookEx(hSkinDialogHook);
hSkinDialogHook=NULL;
}
return bResult;
}
void CSkinDialog::DoGradientFill(CDC *pDC, CRect rect)
{
CBrush pBrush[64];
int nWidth = (rect.right) - (rect.left);
int nHeight = (rect.bottom) - (rect.top);
CRect rct;
BOOL m_bOverControl=FALSE;
BOOL Focus=FALSE;
for (int i=0; i<64; i++)
{
//pBrush[i].CreateSolidBrush(RGB(255-(i/1.3), 255-(i/1.4), 255-(i/1.6)));
pBrush[i].CreateSolidBrush(RGB(100,100,255));
//pBrush[i].CreateSolidBrush(RGB(90+2*i,192+i,104-i));
}
for (i=rect.top; i<nHeight+2; i++)
{
rct.SetRect (rect.left, i, nWidth+2, i + 1);
pDC->FillRect (&rct, &pBrush[((i * 63) / nHeight)]);
}
for (i=0; i<64; i++)
{
pBrush[i].DeleteObject();
}
}
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -