windows 實現一個簡單的自繪邊框,具有最大化,最小化,icon,標題

實現一個簡單的自繪邊框,具有最大化,最小化,icon,標題.

當用戶嫌windows的框架顏色不好看,要修改窗口的框架,就自己在win32上實現它,因爲內容是使用cef框架實現,使用duilib 等界面庫和cef的demo代碼兼容性有些問題.

實現效果:
自繪窗體示例圖片

設計

  • 1 popup 窗口,overlapped 窗口有標題,菜單,一些限制
  • 2 定義客戶區和非客戶區
    目的是在不同的區域返回不同的標誌 。主要在 WM_NCHITTEST 中處理
    分爲:
    1 標題區,去掉 最大,最小,關閉 三個按鈕的上面 20-30 px 的區域,可以拖動窗口
    2 左上右上左下右下,上,下 左 右 ,這些區域都支持拉伸
    3 客戶區,除去這些地方
  • 3 處理 WM_LBUTTONDOW WM_LBUTTONUP WM_MOUSEMOVE WM_MOUSELEAVE
  • 4 處理 WM_NCLBUTTONDBCLK 雙擊最大化,最小化
  • 5 繪製窗口 處理 WM_PAINT
  • 6 處理 WM_SIZE
  • 7 實現按鈕類
  • 8 實現一個icon 類
  • 9 實現窗口的setting類
  • 10 實現窗口管理類

設計完成之後,實現

1 註冊窗口
2 創建窗口
3 窗口過程函數得到窗口類指針,調用類中的函數 OnPaint,OnSize,OnLButtonDown,OnLButtonUp,OnMouseMove,OnNCHitTest
4 定義區域
5 繪製區域

窗口過程函數代碼如下:

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	int wmId, wmEvent;
	PAINTSTRUCT ps;
	HDC hdc;

	switch (message)
	{
	case WM_CREATE:
		__asm  nop;
		CenterWindow(hWnd);
		OnCreate(hWnd);
		break;
	case WM_SIZE:
		OnSize(hWnd,wParam,lParam);
		break;
	case WM_MOUSELEAVE:
		OnMouseLeave(hWnd,wParam,lParam);
		break;
	case WM_MOUSEMOVE:
		OnMouseMove(hWnd,wParam,lParam);
		break;
	
	case WM_NCLBUTTONDBLCLK:
			g_sys_frame.OnNcLButtionDbClk(hWnd,wParam,lParam);
		break;
	case WM_LBUTTONUP:
		OnLButtonUp(hWnd,wParam,lParam);
		break;
	case WM_LBUTTONDOWN:
		OnLButtonDown(hWnd,wParam,lParam);
		break;
	case WM_COMMAND:
		wmId    = LOWORD(wParam);
		wmEvent = HIWORD(wParam);
		// 分析菜單選擇:
		switch (wmId)
		{
		case IDM_ABOUT:
			DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
			break;
		case IDM_EXIT:
			DestroyWindow(hWnd);
			break;
		default:
			return DefWindowProc(hWnd, message, wParam, lParam);
		}
		break;
	case WM_WINDOWPOSCHANGED:
		{
			WINDOWPOS * p = (WINDOWPOS*)lParam;
			int nMinWidth = 500;
			if(p->cx<nMinWidth)
			{
				SetWindowPos(hWnd,0,0,0,nMinWidth,p->cy,SWP_NOMOVE);
			}
			break;
		}
		break;
	case WM_PAINT:
		hdc = BeginPaint(hWnd, &ps);
		OnPaint(hWnd,hdc);
		EndPaint(hWnd, &ps);
		break;
	case WM_NCHITTEST:
		return g_sys_frame.OnNcHitTest(hWnd,wParam,lParam);
		break;
	case WM_DESTROY:
		PostQuitMessage(0);
		break;
	default:
		return DefWindowProc(hWnd, message, wParam, lParam);
	}
	return 0;
}

封裝了一些On函數,這些函數在再調用類實現

void OnPaint(HWND hWnd,HDC hdc)
{
	g_sys_frame.OnPaint(hWnd,hdc);	
}
void OnCreate(HWND wnd)
{
	g_sys_frame.Create(wnd);
}


void OnLButtonDown(HWND hWnd,WPARAM wParam,LPARAM lParam)
{
	g_sys_frame.OnLButtonDown(hWnd,wParam,lParam);
}
void OnLButtonUp(HWND hWnd,WPARAM wParam,LPARAM lParam)
{
	g_sys_frame.OnLButtonUp(hWnd,wParam,lParam);
}
void OnMouseLeave(HWND hWnd,WPARAM wParam,LPARAM lParam)
{
	g_sys_frame.OnMouseLeave(hWnd);
}
void OnMouseMove(HWND hWnd,WPARAM wParam,LPARAM lParam)
{
	g_sys_frame.OnMouseMove(hWnd,wParam,lParam);
}
void OnSize(HWND hWnd,WPARAM wParam,LPARAM lParam)
{
	RECT rcWin;
	GetClientRect(hWnd,&rcWin);
	g_sys_frame.OnSize(hWnd,wParam,lParam);
	InvalidateRect(hWnd,&rcWin,FALSE);
}

g_sys_frame 是一個窗口的實現類

#ifndef system_frame_h
#define system_frame_h
#include "MyCloseBtn.h"
#include "MyMinBtn.h"
#include "MyMaxBtn.h"
#include "MyIcon.h"

//窗口的系統組件
//1 如何處理拉伸
//2 系統的按鈕,最大,最小,關閉,icon,標題

//拉伸的矩形
struct ResizeRect
{
	//拉伸的標誌,HTLEFT ...
	LRESULT resize_flag;
	//拉伸的矩形
	RECT resize_rect;
	ResizeRect(){}
	ResizeRect(RECT &rc,LRESULT flag)
	{
		resize_rect = rc;
		resize_flag = flag;
	}
	void SetResizeRect(RECT &rc,LRESULT flag)
	{
		resize_rect = rc;
		resize_flag = flag;
	}
};


RECT MoveRectEx(RECT &rc,int dx,int dy)
{
	RECT rcTmp=rc;
	OffsetRect(&rcTmp,dx,dy);
	return rcTmp;
}
inline RECT GenRect(int x,int y,int width,int height)
{
	RECT rc={x,y,x+width,y+height};
	return rc;
}
inline void GenResizeRect(RECT &rcWin,vector<ResizeRect> & ret_vec)
{
	const int border_width = sys_frame_settings::get().border_width*4;
	const int x_off = RECT_WIDTH(rcWin)-border_width*2;
	const int y_off = RECT_HEIGHT(rcWin)-border_width*2;
	//左上
	RECT rc={0,0,border_width,border_width};
	ret_vec.push_back(ResizeRect(rc,HTTOPLEFT));
	ret_vec.push_back(ResizeRect(MoveRectEx(rc,x_off,0),HTTOPRIGHT));
	ret_vec.push_back(ResizeRect(MoveRectEx(rc,0,y_off),HTBOTTOMLEFT));
	ret_vec.push_back(ResizeRect(MoveRectEx(rc,x_off,y_off),HTBOTTOMRIGHT));

	//上
	rc = GenRect(border_width,0,x_off,border_width);
	ret_vec.push_back(ResizeRect(rc,HTTOP));
	ret_vec.push_back(ResizeRect(MoveRectEx(rc,0,y_off),HTBOTTOM));
	
	//左
	rc = GenRect(0,border_width,border_width,y_off);
	ret_vec.push_back(ResizeRect(rc,HTLEFT));
	ret_vec.push_back(ResizeRect(MoveRectEx(rc,x_off,0),HTRIGHT));
}


//系統按鈕和icon 標題實現
struct SysButtons
{
	CMyBtn *g_close_btn;
	CMyBtn *g_min_btn;
	CMyBtn *g_max_btn;
	CMyIcon* g_icon;
	int m_mouse_state;
	BOOL m_bEnable;
	void SetEnable(BOOL b)
	{
		m_bEnable = b;
	}
	BOOL isEnable()
	{
		return m_bEnable;
	}
	SysButtons()
	{
		m_bEnable = FALSE;
		g_max_btn=NULL;
		g_min_btn=NULL;
		g_close_btn=NULL;
		g_icon=NULL;
		m_mouse_state = 0;
	}
	void Create(HWND wnd)
	{
		g_close_btn = new CMyCloseBtn();
		g_close_btn->m_parent = wnd;
		RECT rcWin;
		GetClientRect(wnd,&rcWin);
		g_close_btn->CorrectRect(rcWin);

		g_min_btn = new CMyMinBtn();
		g_min_btn->m_parent = wnd;
		g_min_btn->CorrectRect(rcWin);

		g_max_btn = new CMyMaxBtn();
		g_max_btn->m_parent = wnd;
		g_max_btn->CorrectRect(rcWin);

		g_icon = new CMyIcon();

		g_icon->CorrectRect(rcWin);
	}
	void OnLButtonUp(HWND hWnd,WPARAM wParam,LPARAM lParam)
	{
		g_close_btn->OnLButtonUp(hWnd,wParam,lParam);
		g_min_btn->OnLButtonUp(hWnd,wParam,lParam);
		g_max_btn->OnLButtonUp(hWnd,wParam,lParam);
	}
	void OnLButtonDown(HWND hWnd,WPARAM wParam,LPARAM lParam)
	{
		g_close_btn->OnLButtonDown(hWnd,wParam,lParam);
		g_min_btn->OnLButtonDown(hWnd,wParam,lParam);
		g_max_btn->OnLButtonDown(hWnd,wParam,lParam);
	}
	void OnMouseMove(HWND hWnd,WPARAM wParam,LPARAM lParam)
	{
		if(m_mouse_state==0)
		{
			TRACKMOUSEEVENT te={sizeof(TRACKMOUSEEVENT ),TME_LEAVE,hWnd};
			TrackMouseEvent(&te);

			m_mouse_state = 1;
		}
		g_close_btn->OnMouseMove(hWnd,wParam,lParam);
		g_min_btn->OnMouseMove(hWnd,wParam,lParam);
		g_max_btn->OnMouseMove(hWnd,wParam,lParam);
	}
	void OnSize(HWND hWnd,WPARAM wParam,LPARAM lParam)
	{
		CorrectBtns(hWnd);
	}
	void OnMouseLeave(HWND hWnd)
	{
		m_mouse_state = 0;
		g_close_btn->OnMouseLeave(hWnd);
		g_min_btn->OnMouseLeave(hWnd);
		g_max_btn->OnMouseLeave(hWnd);
	}
	LRESULT OnNcHitTest(HWND hWnd,WPARAM wParam,LPARAM lParam)
	{

		int x;
		int y;
		x = LOWORD(lParam);
		y = HIWORD(lParam);
		RECT rcWin;
		GetClientRect(hWnd,&rcWin);
		RECT rcTitle;
		sys_frame_settings::get().GetTitleRect(rcWin,rcTitle);
		POINT pt={x,y};
		ScreenToClient(hWnd,&pt);
		CheckAndRedraw(hWnd,pt);

		if(PtInCaption(hWnd,wParam,lParam))
		{
			return HTCAPTION;
		}
		vector<ResizeRect> vec;
		GenResizeRect(rcWin,vec);
		for(size_t i=0;i<vec.size();i++)
		{
			ResizeRect &rr = vec[i];
			if(PtInRect(&rr.resize_rect,pt))
			{
				return rr.resize_flag;
			}
		}

		LRESULT result =  DefWindowProc(hWnd,WM_NCHITTEST,wParam,lParam);
		return result;
	}
	void CheckAndRedraw(HWND hWnd,POINT pt)
	{
		if(!g_min_btn->PtInRect(pt))
		{
			g_min_btn->SetState(BTN_STATE_NORMAL);
			if(g_min_btn->isChanged())
			{
				InvalidateRect(hWnd,&g_min_btn->m_rt,TRUE);
			}
		}
		if(!g_max_btn->PtInRect(pt))
		{
			g_max_btn->SetState(BTN_STATE_NORMAL);
			if(g_max_btn->isChanged())
			{
				InvalidateRect(hWnd,&g_max_btn->m_rt,TRUE);
			}
		}
		if(!g_close_btn->PtInRect(pt))
		{
			g_close_btn->SetState(BTN_STATE_NORMAL);
			if(g_close_btn->isChanged())
			{
				InvalidateRect(hWnd,&g_close_btn->m_rt,TRUE);
			}
		}
	}
	HRGN GenFromRect(RECT &rc)
	{
		return CreateRectRgn(rc.left,rc.top,rc.right,rc.bottom);
	}
	void CorrectBtns(HWND hWnd)
	{
		RECT rcWin;
		GetClientRect(hWnd,&rcWin);
		g_max_btn->CorrectRect(rcWin);
		g_min_btn->CorrectRect(rcWin);
		g_close_btn->CorrectRect(rcWin);
		g_icon->CorrectRect(rcWin);
	}
	HRGN GetRgn(HWND hWnd)
	{
		CorrectBtns(hWnd);

		HRGN rgnMax = GenFromRect(g_max_btn->m_rt);
		HRGN rgnMin = GenFromRect(g_min_btn->m_rt);
		HRGN rgnClose = GenFromRect(g_close_btn->m_rt);
		CombineRgn(rgnMax,rgnMax,rgnMin,RGN_OR);
		CombineRgn(rgnMax,rgnMax,rgnClose,RGN_OR);
		DeleteObject(rgnMin);
		DeleteObject(rgnClose);
		return rgnMax;
	}
	void OnPaint(HWND hWnd,HDC hdc)
	{
		//繪製邊框
		RECT rcWin;
		GetClientRect(hWnd,&rcWin);
		HBRUSH br = CreateSolidBrush(sys_frame_settings::get().border_corlor);
		for(int i=0; i<sys_frame_settings::get().border_width; i++)
		{   
			FrameRect(hdc, &rcWin, br);   
			InflateRect(&rcWin, -1, -1);           
		} 
		DeleteObject(br);
		//繪製標題背景
		RECT rcTitle=rcWin;
		//InflateRect(&rcTitle,-4,-4);
		rcTitle.bottom=rcTitle.top+sys_frame_settings::get().title_height;
		HBRUSH title_br = CreateSolidBrush(sys_frame_settings::get().title_bk_color);
		FillRect(hdc,&rcTitle,title_br);
		DeleteObject(title_br);
		//繪製標題
		TCHAR title[256]={0};
		GetWindowText(hWnd,title,ARRAYSIZE(title));
		SetBkMode(hdc,TRANSPARENT);
		sys_frame_settings::get().GetCaptionRect(rcWin,rcTitle);
		SetTextColor(hdc,sys_frame_settings::get().title_text_color);
		DrawText(hdc,title,-1,&rcTitle,DT_SINGLELINE|DT_VCENTER);

		Draw(hdc);
	}
	void Draw(HDC hdc)
	{
		g_close_btn->Draw(hdc);
		g_min_btn->Draw(hdc);
		g_max_btn->Draw(hdc);
		g_icon->Draw(hdc);
	}
	void SetMaximizeState(HWND hWnd)
	{
		g_max_btn->SetMaximizeState(hWnd);
	}
	BOOL PtInCaption(HWND hWnd,WPARAM wParam,LPARAM lParam)
	{
		int x;
		int y;
		x = LOWORD(lParam);
		y = HIWORD(lParam);
		RECT rcWin;
		GetClientRect(hWnd,&rcWin);
		RECT rcTitle;
		sys_frame_settings::get().GetTitleRect(rcWin,rcTitle);
		POINT pt={x,y};
		ScreenToClient(hWnd,&pt);
		{
			RECT rt = g_close_btn->m_rt;
			WCHAR msg[256]={};
			wsprintf(msg,L"%d,%d,(%d,%d)-(%d,%d)",pt.x,pt.y,rt.left,rt.top,rt.right,rt.bottom);
			OutputDebugString(msg);
		}

		HRGN rgn = CreateRectRgn(rcTitle.left,rcTitle.top,rcTitle.right,rcTitle.bottom);
		HRGN rgnSysBtns = GetRgn(hWnd);

		int ret=CombineRgn(rgn,rgn,rgnSysBtns,RGN_DIFF);
		if(ret!=NULLREGION && ret!=ERROR)
		{
			if(PtInRegion(rgn,pt.x,pt.y))
			{
				DeleteObject(rgn);
				DeleteObject(rgnSysBtns);
				return TRUE;
			}
		}

		DeleteObject(rgn);
		DeleteObject(rgnSysBtns);
		return FALSE;
	}
	void OnNcLButtionDbClk(HWND hWnd,WPARAM wParam,LPARAM lParam)
	{
		if(wParam == HTCAPTION)
		{
			SetMaximizeState(hWnd);
		}

	}
};

#endif

在這裏調用類 CMyBtn 一些派生類 CMyMinBtn CMyIcon CMyMaxBtn CMyCloseBtn 實現按鈕繪製和icon繪製

代碼如下

#pragma once
/*
狀態 : normal over down
顯示 : 背景色,點陣數組,點陣顏色

*/
enum
{
	BTN_STATE_NORMAL,
	BTN_STATE_OVER,
	BTN_STATE_DOWN
};
enum {
	MAX_STATE_NORMAL,
	MAX_STATE_MAXIZED,	
};
enum {
	MOUSE_STATE_OFF,
	MOUSE_STATE_ON,
};
#include "win_settings.h"
inline int RECT_WIDTH(RECT &rc){return rc.right-rc.left;}

inline int RECT_HEIGHT(RECT &rc){return rc.bottom-rc.top;}

class CMyBtn
{
public:
	CMyBtn(void);
	~CMyBtn(void);
//
	int m_state;
	int m_old_state;
	RECT m_rt;
	HWND m_parent;
	BOOL m_bDown;
	void SetParent(HWND wnd)
	{
		m_parent = wnd;
	}
	void SetState(int state)
	{
		m_old_state = m_state;
		m_state = state;
	}
	bool isChanged()
	{
		return m_state != m_old_state;
	}
	int GetState()
	{
		return m_state;
	}
	void SetDownFlag(BOOL b)
	{
		m_bDown = b;
	}
	bool PtInRect(POINT pt)
	{
		return ::PtInRect(&m_rt,pt)?true:false;
	}
	int GetMaximizeState()
	{
		return m_max_state;
	}
	void SetMaximizeState(HWND hWnd)
	{
		if(m_max_state==MAX_STATE_NORMAL)
		{
			PostMessage(hWnd,WM_SYSCOMMAND,SC_MAXIMIZE,0);			
			m_max_state = MAX_STATE_MAXIZED;
		}
		else
		{
			PostMessage(hWnd,WM_SYSCOMMAND,SC_RESTORE,0);
			m_max_state = MAX_STATE_NORMAL;
		}

	}
	void SetMinize(HWND hWnd)
	{
		PostMessage(hWnd,WM_SYSCOMMAND,SC_MINIMIZE,0);
	}
	virtual BOOL OnLButtonUp(HWND hWnd,WPARAM wParam,LPARAM lParam)
	{
		int x;
		int y;
		x =  LOWORD(lParam); 
		y = HIWORD(lParam);
		POINT pt={x,y};
		if(PtInRect(pt))
		{
			SetState(BTN_STATE_NORMAL);
			InvalidateRect(hWnd,&m_rt,TRUE);
			SetDownFlag(FALSE);
			return TRUE;
		}
		return FALSE;
	}
	virtual void OnMouseLeave(HWND hWnd)
	{
		SetState(BTN_STATE_NORMAL);
		InvalidateRect(hWnd,&m_rt,TRUE);
	}
	virtual void OnMouseMove(HWND hWnd,WPARAM wParam,LPARAM lParam)
	{
		int x;
		int y;
		x =  LOWORD(lParam); 
		y = HIWORD(lParam);
		POINT pt={x,y};
		if(PtInRect(pt))
		{
			SetState(BTN_STATE_OVER);
			InvalidateRect(hWnd,&m_rt,TRUE);
		}
		else
		{
			SetState(BTN_STATE_NORMAL);
			if(isChanged())
			{
				InvalidateRect(hWnd,&m_rt,TRUE);
			}
		}
	}
	virtual BOOL OnLButtonDown(HWND hWnd,WPARAM wParam,LPARAM lParam)
	{
		int x;
		int y;
		x =  LOWORD(lParam); 
		y = HIWORD(lParam);
		POINT pt={x,y};
		if(PtInRect(pt))
		{
			SetState(BTN_STATE_DOWN);
			InvalidateRect(hWnd,&m_rt,TRUE);
			SetDownFlag(TRUE);
			return TRUE;
		}
		return FALSE;
	}
	virtual void CorrectRect(RECT &rcWin)
	{
		
	}
	int m_max_state;

	virtual void Draw(HDC hdc);
};

派生類

#pragma once
#include "mybtn.h"
class CMyMaxBtn :
	public CMyBtn
{
public:
	CMyMaxBtn(void);
	~CMyMaxBtn(void);
	virtual void Draw(HDC hdc);
	virtual BOOL OnLButtonUp(HWND hWnd,WPARAM wParam,LPARAM lParam);
	virtual void CorrectRect(RECT &rcWin);
	BYTE m_maxize_pic[100];
	BYTE m_normal_pic[100];
};

#pragma once
#include "mybtn.h"

class CMyMinBtn :
	public CMyBtn
{
public:
	CMyMinBtn(void);
	~CMyMinBtn(void);
	virtual void Draw(HDC hdc);
	virtual BOOL OnLButtonUp(HWND hWnd,WPARAM wParam,LPARAM lParam);
	virtual void CorrectRect(RECT &rcWin);
};

#pragma once
#include "mybtn.h"

class CMyCloseBtn :
	public CMyBtn
{
public:
	CMyCloseBtn(void);
	~CMyCloseBtn(void);
	virtual void Draw(HDC hdc);
	virtual BOOL OnLButtonUp(HWND hWnd,WPARAM wParam,LPARAM lParam);
	virtual void CorrectRect(RECT &rcWin);
	unsigned char m_normal_pic[100];
};

還有一個總的setting 類,裏面定義,窗口的背景色,邊框顏色,邊框寬度,字體等等

#ifndef _settings_h_
#define _settings_h_
//窗體的配置參數
//注意:邊框過窄會導致窗口改變大小不方便
class sys_frame_settings
{
public:
	sys_frame_settings()
	{
		border_corlor = RGB(128,128,128);
		title_bk_color = RGB(2,167,240);
		title_text_color= RGB(255,255,255);
		title_height = 23;
		border_width = 1;

	}
	static sys_frame_settings & get(){
		static sys_frame_settings a;
		return a;
	}
	void setWinRect(RECT &rt)
	{
		rcWin = rt;
	}
	void SetTitleRect(RECT &rt)
	{
		rcTitle = rt;
	}
	void GetTitleRect(RECT &rcWin,RECT &rcTitle)
	{
		rcTitle = rcWin;
		InflateRect(&rcTitle,-1*border_width,-1*border_width);
		rcTitle.bottom = rcTitle.top+title_height;
	}
	void GetCaptionRect(RECT &rcWin,RECT &ret_rt)
	{
		ret_rt = rcWin;
		InflateRect(&ret_rt,-1*border_width,-1*border_width);
		ret_rt.left = ret_rt.left+10+16;
		ret_rt.bottom=ret_rt.top+title_height;
	}
	//邊框色
	COLORREF border_corlor;
	//標題背景色
	COLORREF title_bk_color;
	//
	COLORREF title_text_color;
	//標題高度
	int title_height;
	//標題位置和大小
	RECT rcTitle;
	//窗口位置和大小
	RECT rcWin;
	//關閉按鈕
	RECT rcClose;
	//最大化按鈕
	RECT rcMax;
	//最小化按鈕
	RECT rcMin;
	//邊框寬度
	int border_width;
};


#endif

此程序關鍵是如下的知識:

  • 窗體的知識: 客戶區非客戶區
  • 鼠標消息處理
  • 區域
  • 繪畫
  • 窗口的一些基礎知識,窗口類,註冊,創建。
  • c++一些知識
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章