mfc简单换肤

本文介绍了一个MFC程序的换肤库实现方法,通过WH_CBT钩子实现在窗口创建和销毁前后进行子类化处理,以改变窗口的外观。文章详细展示了如何通过自定义绘图函数来美化界面元素。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

      mfc程序的界面一向是大家所诟病的,我之前在写微笔记的时候一直痛心界面的丑陋,所以我下定决定自己写一个换肤的库,虽然有不少bug,但是毕竟是自己的成果,还是蛮开心的。

MySkin.h

#pragma warning(disable:4786)
#include <map>
#ifndef ULONG_PTR
//#define ULONG_PTR unsigned long*
#include "GdiPlus.h"
using namespace Gdiplus;
#endif
#pragma comment(lib,"gdiplus.lib")
using namespace std;
void InitMySkin();
void UninitMySkin();
LRESULT CALLBACK
_MySkinHookFunc(int code, WPARAM wParam, LPARAM lParam);
LRESULT CALLBACK _MySkinListBoxProc(          HWND hwnd,
									UINT uMsg,
									WPARAM wParam,
									LPARAM lParam
						 );
LRESULT CALLBACK _MySkinDialogProc(          HWND hwnd,
								   UINT uMsg,
								   WPARAM wParam,
								   LPARAM lParam
						 );
LRESULT CALLBACK _MySkinButtonProc(          HWND hwnd,
									UINT uMsg,
									WPARAM wParam,
									LPARAM lParam
						 );
LRESULT CALLBACK _MySkinStaticProc(          HWND hwnd,
								   UINT uMsg,
								   WPARAM wParam,
								   LPARAM lParam
						 );
LRESULT CALLBACK _MySkinEditProc(          HWND hwnd,
								   UINT uMsg,
								   WPARAM wParam,
								   LPARAM lParam
						 );

      MySkin.cpp

#include "StdAfx.h"
#include "MySkin.h"
map<HWND,WNDPROC> g_mOldFunc;
HHOOK g_hMySkinHook;
GdiplusStartupInput g_gdiplusStartupInput;
 ULONG_PTR g_pGdiToken;
#define WM_MYDRAWITEM WM_USER+1
#define  WM_MYCTLCOLOR WM_USER+2
#define  WM_MYMEASUREITEM WM_USER+3
 #define  WM_GIFSTOP WM_USER+3
void InitMySkin()
{
	g_hMySkinHook=SetWindowsHookEx(WH_CBT,_MySkinHookFunc,0,GetCurrentThreadId());
	GdiplusStartup(&g_pGdiToken,&g_gdiplusStartupInput,NULL);
}
void UninitMySkin()
{
	UnhookWindowsHookEx(g_hMySkinHook);
	GdiplusShutdown(g_pGdiToken);
}
LRESULT CALLBACK
_MySkinHookFunc(int code, WPARAM wParam, LPARAM lParam)
{
	if(code==HCBT_CREATEWND)
	{
		HWND hWin=(HWND)wParam;
		CBT_CREATEWND* s=(CBT_CREATEWND*)lParam;
		TCHAR szClass[255]={0};
		GetClassName(hWin,szClass,255);
		
		if(_tcsicmp(szClass,"listbox")==0)
		{
			int nStyle=GetWindowLong(hWin,GWL_STYLE);
			if(!(nStyle & LBS_OWNERDRAWFIXED))
			{
				SetWindowLong(hWin,GWL_STYLE,GetWindowLong(hWin,GWL_STYLE)|LBS_OWNERDRAWFIXED|LBS_HASSTRINGS);
				g_mOldFunc[hWin]=(WNDPROC)SetWindowLong(hWin,GWL_WNDPROC,(long)_MySkinListBoxProc);
			}
	
		}else if(_tcsicmp(szClass,_T("#32770"))==0)
		{
			SetWindowLong(hWin,GWL_STYLE,GetWindowLong(hWin,GWL_STYLE)&~WS_SYSMENU);
			g_mOldFunc[hWin]=(WNDPROC)SetWindowLong(hWin,GWL_WNDPROC,(long)_MySkinDialogProc);
			
		}else 	if(_tcsicmp(szClass,_T("button"))==0)
		{
			int nStyle=GetWindowLong(hWin,GWL_STYLE);
			if(!(nStyle & BS_OWNERDRAW))
			{
				nStyle=(GetWindowLong(hWin,GWL_STYLE) & BS_TYPEMASK);
				if(nStyle== BS_PUSHBUTTON || nStyle==BS_DEFPUSHBUTTON)
				{
					SetWindowLong(hWin,GWL_STYLE,GetWindowLong(hWin,GWL_STYLE)|BS_OWNERDRAW);
					g_mOldFunc[hWin]=(WNDPROC)SetWindowLong(hWin,GWL_WNDPROC,(long)_MySkinButtonProc);

				}
			}
		}else 	if(_tcsicmp(szClass,_T("static"))==0)
		{
			int nStyle=GetWindowLong(hWin,GWL_STYLE);
			if(!(nStyle & SS_OWNERDRAW))
			{
				g_mOldFunc[hWin]=(WNDPROC)SetWindowLong(hWin,GWL_WNDPROC,(long)_MySkinStaticProc);
			}
		}else 	if(_tcsicmp(szClass,_T("edit"))==0)
		{
				g_mOldFunc[hWin]=(WNDPROC)SetWindowLong(hWin,GWL_WNDPROC,(long)_MySkinEditProc);
		}
		return 0;
	}
	else if(code==HCBT_DESTROYWND)
	{
		HWND hWin=(HWND)wParam;
		if(g_mOldFunc[hWin])
		{
			SetWindowLong(hWin,GWL_WNDPROC,(long)g_mOldFunc[hWin]);
			g_mOldFunc.erase(hWin);
		}
	}
	return CallNextHookEx(g_hMySkinHook,code,wParam,lParam);
}
LRESULT CALLBACK _MySkinDialogProc(          HWND hwnd,
								   UINT uMsg,
								   WPARAM wParam,
								   LPARAM lParam
								   )
{
	if(uMsg==WM_MEASUREITEM)
	{
		LPMEASUREITEMSTRUCT ms=(LPMEASUREITEMSTRUCT)lParam;
		HWND hWin=GetDlgItem(hwnd,ms->CtlID);
		SendMessage(hWin,WM_MYMEASUREITEM,wParam,lParam);
		return 0;
	}
	else if(uMsg==WM_DRAWITEM)
	{
		LPDRAWITEMSTRUCT ds=(LPDRAWITEMSTRUCT)lParam;
		HWND h=GetDlgItem(hwnd,ds->CtlID);
		::SendMessage(ds->hwndItem,WM_MYDRAWITEM,wParam,lParam);
		return 0;
	}
	else if(uMsg==WM_CTLCOLOREDIT)
	{
		HBRUSH b=(HBRUSH)::SendMessage(HWND(lParam),WM_MYCTLCOLOR,wParam,lParam);
		return (long)b;
	}else if(uMsg==WM_CTLCOLORDLG)
	{
		CDC dc;
		dc.Attach((HDC)wParam);
		static CBrush b;
		static bool bFont=false;
		b.DeleteObject();
		b.CreateSolidBrush(RGB(253,255,240));
		dc.Detach();
		return (long)(HBRUSH)b;
	}
	else if(uMsg==WM_NCPAINT)
	{
		HDC hdc=GetWindowDC(hwnd);
		CDC *dc=CDC::FromHandle(hdc);
		CRect r1,r2,rLeft,rTop,rRight,rBottom;
		GetWindowRect(hwnd,r1);
		
		GetClientRect(hwnd,&r2);
		CWnd::FromHandle(hwnd)->ClientToScreen(&r2);
		rLeft.left=0;
		rLeft.top=0;
		rLeft.right=r2.left-r1.left;
		rLeft.bottom=r1.Height();
		rTop.left=0;
		rTop.top=0;
		rTop.right=r1.Width();
		rTop.bottom=r2.top-r1.top;
		rRight.left=r2.left-r1.left+r2.Width();
		rRight.top=0;
		rRight.right=r1.Width();
		rRight.bottom=r1.Height();
		rBottom.left=0;
		rBottom.top=r2.top-r1.top+r2.Height();
		rBottom.right=r1.Width();
		rBottom.bottom=r1.Height();
		dc->FillSolidRect(&rLeft,RGB(113,113,113));
		dc->FillSolidRect(&rTop,RGB(113,113,113));
		dc->FillSolidRect(&rRight,RGB(113,113,113));
		dc->FillSolidRect(&rBottom,RGB(113,113,113));
		CRect rClose;
		rClose.left=r1.Width()-30;
		rClose.right=r1.Width()-10;
		rClose.top=3;
		rClose.bottom=28;
		dc->Draw3dRect(&rClose,RGB(250,250,250),RGB(250,250,250));
		dc->SetBkMode(TRANSPARENT);
		dc->SetTextColor(RGB(250,250,250));
		dc->DrawText(_T("X"),&rClose,DT_CENTER|DT_VCENTER|DT_SINGLELINE);
		HWND hPar=GetParent(hwnd);
		if(hPar==GetDesktopWindow() || IsWindowEnabled(hPar) || hwnd==AfxGetMainWnd()->m_hWnd)
		{
			CRect rMini;
			rMini.left=rClose.left-25;
			rMini.right=rClose.left-5;
			rMini.top=3;
			rMini.bottom=28;
			dc->Draw3dRect(&rMini,RGB(250,250,250),RGB(250,250,250));
			dc->DrawText(_T("_"),&rMini,DT_CENTER|DT_VCENTER|DT_SINGLELINE);
		}
		TCHAR szTitle[_MAX_PATH]={0};
		GetWindowText(hwnd,szTitle,_MAX_PATH);
		dc->TextOut(5,10,szTitle);
		ReleaseDC(hwnd,hdc);
		return 0;
	}else if(uMsg==WM_NCLBUTTONDOWN)
	{
		CPoint p;
		GetCursorPos(&p);
		CRect r,r1,r2;
		GetWindowRect(hwnd,&r);
		CRect rClose;
		rClose.left=r.Width()-30;
		rClose.right=r.Width()-10;
		rClose.top=3;
		rClose.bottom=28;
		r1=rClose;
		CRect rMini;
		rMini.left=rClose.left-25;
		rMini.right=rClose.left-5;
		rMini.top=3;
		rMini.bottom=28;
		r2=rMini;
		r1.OffsetRect(r.TopLeft());
		r2.OffsetRect(r.TopLeft());
		if(PtInRect(&r1,p))
		{
			PostMessage(hwnd,WM_SYSCOMMAND,SC_CLOSE,MAKELPARAM(p.x,p.y));
			return 0;
		}
		HWND hPar=GetParent(hwnd);
		if(hPar==GetDesktopWindow() || IsWindowEnabled(hPar) || hwnd==AfxGetMainWnd()->m_hWnd)
		{
			if(PtInRect(&r2,p))
			{
				PostMessage(hwnd,WM_SYSCOMMAND,SC_MINIMIZE,MAKELPARAM(p.x,p.y));
				return 0;
		}
		}
	}else if(uMsg==WM_NCACTIVATE)
	{
		SendMessage(hwnd,WM_NCPAINT,0,0);
		return 1;
	}else if(uMsg==WM_SETTEXT)
	{
		CallWindowProc(g_mOldFunc[hwnd],hwnd,uMsg,wParam,lParam);
		SendMessage(hwnd,WM_NCPAINT,0,0);
		return 1;
	}
	return CallWindowProc(g_mOldFunc[hwnd],hwnd,uMsg,wParam,lParam);
	
}
LRESULT CALLBACK _MySkinListBoxProc(          HWND hwnd,
									UINT uMsg,
									WPARAM wParam,
									LPARAM lParam
									)
{
		if(uMsg==WM_ERASEBKGND)
		{
			CDC dc;
			dc.Attach((HDC)wParam);
			CRect r;
			GetClientRect(hwnd,&r);
			dc.FillSolidRect(&r,RGB(255,255,176));
			dc.Detach();
			return 0;
		}
		else if(uMsg==WM_MYMEASUREITEM)
		{
			LPMEASUREITEMSTRUCT ms=(LPMEASUREITEMSTRUCT)lParam;
			ms->itemHeight=30;
			return 0;
		}
		else if(uMsg==WM_MYDRAWITEM)
		{
			LPDRAWITEMSTRUCT ds=(LPDRAWITEMSTRUCT)lParam;
			CDC dc;
			dc.Attach(ds->hDC);
			CRect r=ds->rcItem;
			if( (ds->itemState & ODS_SELECTED))
			{
				dc.FillSolidRect(r.left,r.top,r.Width(),r.Height(),RGB(227,251,251));
				dc.DrawEdge(&r,EDGE_SUNKEN,BF_BOTTOM);
			}
			else/* if((ds->itemAction & ODA_DRAWENTIRE)  || !(ds->itemState & ODS_SELECTED))*/
			{
				dc.FillSolidRect(r.left,r.top,r.Width(),r.Height(),RGB(255,255,176));
				dc.DrawEdge(&r,EDGE_RAISED,BF_BOTTOM);
			}
			CPen p;
			p.CreatePen(PS_SOLID,2,RGB(92,92,92));
			CPen *oldPen=dc.SelectObject(&p);
			dc.MoveTo(r.left,r.bottom-1);
			dc.LineTo(r.right,r.bottom-1);
			dc.SelectObject(oldPen);
			TCHAR txt[255]={0};
			SendMessage(ds->hwndItem,LB_GETTEXT,ds->itemID,(long)txt);
			dc.SetBkMode(TRANSPARENT);
			//	dc.SetBkColor(RGB(222,22,2));
			dc.SetTextColor(RGB(111,111,11));
			CFont f;
			f.CreatePointFont(120,_T("微软雅黑"));
			CFont *oldFont=dc.SelectObject(&f);
			dc.DrawText(txt,strlen(txt),&(ds->rcItem),DT_CENTER|DT_VCENTER|DT_SINGLELINE);
			dc.SelectObject(oldFont);
			dc.Detach();
			return 0;
		}
	return CallWindowProc(g_mOldFunc[hwnd],hwnd,uMsg,wParam,lParam);
}
LRESULT CALLBACK _MySkinButtonProc(          HWND hwnd,
								   UINT uMsg,
								   WPARAM wParam,
								   LPARAM lParam
						 )
{
		if(uMsg==WM_MYDRAWITEM)
		{
			LPDRAWITEMSTRUCT ds=(LPDRAWITEMSTRUCT)lParam;
			CDC dc;
			CRect r=ds->rcItem;
			dc.Attach(ds->hDC);
			dc.Draw3dRect(&ds->rcItem,RGB(2,25,25),RGB(10,0,10)); 
			 dc.FillSolidRect(&ds->rcItem,RGB(143,143,216));//Here you can define the required color to appear on the Button.
			 UINT state=ds->itemState; //This defines the state of the Push button either pressed or not. 
			 if((state & ODS_SELECTED))
			 {
				 dc.DrawEdge(&ds->rcItem,EDGE_SUNKEN,BF_RECT);			 
			 }
			 else
			 {
				 dc.DrawEdge(&ds->rcItem,EDGE_RAISED,BF_RECT);	
			 }
			 dc.SetBkMode(TRANSPARENT);
			 dc.SetTextColor(RGB(0,0,0));     //Setting the Text Color
			 
			 TCHAR buffer[MAX_PATH];           //To store the Caption of the button.
			 ZeroMemory(buffer,MAX_PATH );     //Intializing the buffer to zero
			 ::GetWindowText(ds->hwndItem,buffer,MAX_PATH); //Get the Caption of Button Window 
			 
    dc.DrawText(buffer,&ds->rcItem,DT_CENTER|DT_VCENTER|DT_SINGLELINE);//Redraw the Caption of Button Window 
	dc.Detach();
				return 0;
		}
return CallWindowProc(g_mOldFunc[hwnd],hwnd,uMsg,wParam,lParam);
}
LRESULT CALLBACK _MySkinStaticProc(          HWND hwnd,
								   UINT uMsg,
								   WPARAM wParam,
								   LPARAM lParam
						 )
{
	if(uMsg==WM_PAINT)
	{
		HWND hPar=GetParent(hwnd);
		HDC pDc=GetDC(hPar);
		COLORREF color=GetBkColor(pDc);
		ReleaseDC(hPar,pDc);
		CPaintDC dc(CWnd::FromHandle(hwnd));
		CRect r;
		GetClientRect(hwnd,&r);
		dc.FillSolidRect(&r,color);
		TCHAR szText[_MAX_PATH]={0};
		GetWindowText(hwnd,szText,_MAX_PATH);
		CFont *ff=dc.GetCurrentFont();
		LOGFONT lf;
		memset(&lf, 0, sizeof(LOGFONT));
		lf.lfCharSet = DEFAULT_CHARSET;
		ff->GetLogFont(&lf);
		if(lf.lfHeight+2>r.Height())
		{
			lf.lfHeight=r.Height()+2;
		}
		else
		{
			lf.lfHeight=lf.lfHeight+2;
		}
		_tcscpy(lf.lfFaceName,_T("微软雅黑"));
		CFont f;
		f.CreateFontIndirect(&lf);
	//	f.CreatePointFont(90,_T("微软雅黑"));
		CFont *oldFont=dc.SelectObject(&f);
		dc.SetBkMode(TRANSPARENT);
		dc.SetTextColor(RGB(7,126,152));
		int height=dc.DrawText(szText,&r,DT_EDITCONTROL|DT_WORDBREAK|DT_CALCRECT); 
		//r.DeflateRect(0,height);
		dc.DrawText(szText,&r,DT_EDITCONTROL|DT_WORDBREAK); 
		dc.SelectObject(oldFont);
		return 0;
	}
	return CallWindowProc(g_mOldFunc[hwnd],hwnd,uMsg,wParam,lParam);
}
LRESULT CALLBACK _MySkinEditProc(          HWND hwnd,
								 UINT uMsg,
								 WPARAM wParam,
								 LPARAM lParam
						 )
{
	if(uMsg==WM_MYCTLCOLOR)
	{
		CDC dc;
		dc.Attach((HDC)wParam);
		static CBrush b;
		static bool bFont=false;
		b.DeleteObject();
		b.CreateSolidBrush(RGB(181,207,230));
		dc.SetBkMode(TRANSPARENT);
		dc.SetBkColor(RGB(181,207,230));
		dc.SetTextColor(RGB(88,3,46));
		dc.Detach();
		if(!bFont)
		{
			static CFont f;
			f.CreatePointFont(120,_T("微软雅黑"));
			SendMessage(hwnd,WM_SETFONT,(WPARAM)(HFONT)f,MAKELPARAM(1,0));
			bFont=1;
		}
		return (long)(HBRUSH)b;
	}
	return CallWindowProc(g_mOldFunc[hwnd],hwnd,uMsg,wParam,lParam);
}


       这个换肤是挂钩WH_CBT钩子,这个钩子会在窗口的创建前和销毁前响应,正好适合我们在这里子类化来修改新窗口的消息处理函数,我们把修改的窗口保存到一个map里,然后当窗口销毁时还原原来的消息处理函数,在绘制的过程中,CStatic我用的是wm_paint消息,而其他的控件我用的是owner draw。在这里我发现一点,就是在一个窗体owner draw之前会判断当前有没有无效区,也就是调用beginpaint函数,要是调用了,就不会owner draw,因为子窗体在wm_paint里已经自我绘制了,就不需要交给父窗体来帮他绘制。

        换肤很简单,在mfc的InitInstance中,主窗体doModal前调用InitMySkin()即可,释放在ExitInstance中调用UninitMySkin()即可。

        本文有不足之处,还望大家多多指正。
   

    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值