實現一個簡單的自繪邊框,具有最大化,最小化,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++一些知識