⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 skindialog.cpp

📁 机械手IPC控制器伺服运动程序
💻 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 + -