一个简单的标题栏自绘
一:步骤
(1)新建一个基于对话框的MFC程序,打开对话框属性设置界面,去掉对话框的标题栏和边界BORDER(vc和vs的设置相似,这里使用的是vs2015,界面与vc稍有不同),
(2)在客户曲自绘一个标题栏和关闭按钮,需要使用到WM_PAINT消息的处理函数,使用Class WIZARD添加OnPaint()消息相应函数
(3)为了使自绘的标题栏能够用鼠标拖动窗口,并且使自绘的关闭按钮能够关闭界面,需要使用到OnNcHitTest(…)消息处理函数和OnLButtonDown(…)消息处理函数
二:函数解释:
(1)LRESULT CTest2Dlg::OnNcHitTest(CPoint point)
功能:每当鼠标移动时,框架就为包含光标(或者是用SetCapture成员函数捕获了鼠标输入的CWnd对象)的CWnd对象调用这个成员函数
参数:point,当前鼠标的坐标,相对于屏幕坐标系
返回值:表示点击位置的类型,有以下类型
HTBORDER:在不具有可变大小边框的窗口的边框上
HTBOTTOM:在窗口的水平边框的底部
HTBOTTOMLEFT:在窗口边框的左下角
HTBOTTOMRIGHT:在窗口边框的右下角
HTCAPTION:在标题条中
HTCLIENT:在客户区中
HTERROR:HTERROR 在屏幕背景或窗口之间的分隔线上(与HTNOWHERE相同,除了 Windows的DefWndProc函数产生一个系统响声以指明错误)
HTGROWBOX:在尺寸框中
HTHSCROLL:在水平滚动条上
HTLEFT:在窗口的左边框上
HTMAXBUTTON:在最大化按钮上
HTMENU:在菜单区域
HTMINBUTTON:在最小化按钮上
HTNOWHERE:在屏幕背景或窗口之间的分割线上
HTREDUCE:在最小化按钮上
HTRIGHT:在窗口的右边框上
HTSIZE:在尺寸框中(与HTGROWBOX相同)
HTSYSMENU:在控制菜单或子窗口的关闭按钮上
HTTOP:在窗口水平边框的上方
HTTOPLEFT:在窗口水平边框的左上角
HTTOPRIGHT:在窗口边框的右上角
HTTRANSPARENT:在一个被其他窗口覆盖的窗口中
HTVSCROLL:在垂直滚动条中
HTZOOM:在最大化按钮上
(2)void CTest2Dlg::OnLButtonDown(UINT nFlags, CPoint point)
功能:当鼠标点击自绘的关闭按钮时,关闭窗口
参数:nFlags,指示各种虚拟键是否按下。此参数可以是下列值的任意组合:
如果按下CTRL键,MK_CONTROL 设置。
如果鼠标左键滚动,MK_LBUTTON 设置。
如果元鼠标按钮处于按下,MK_MBUTTON 设置。
如果鼠标右键滚动,MK_RBUTTON 设置。
如果SHIFT键下降,MK_SHIFT 设置。
point
指定光标的x坐标和y坐标。这些坐标始终是相对于窗口左上角的。
(3) BOOL PtInRect( POINT point ) const throw( );
功能:检测某个点是否在某个矩形中
参数:point
包含POINT 结构或 CPoint 对象。
返回值
非零,则点在 CRect之间;否则为0。
(4)CDC::DrawText
enter description here
三:代码
Test2Dlg.h头文件,添加一个存放关闭按钮矩形位置的CRect对象m_rtBtnClose;然后使用类相对添加相关消息相应函数
// Test2Dlg.h : header file
//
#pragma once
// CTest2Dlg dialog
class CTest2Dlg : public CDialogEx
{
public:
CRect m_rtBtnClose;
// Construction
public:
CTest2Dlg(CWnd* pParent = NULL); // standard constructor
// Dialog Data
#ifdef AFX_DESIGN_TIME
enum { IDD = IDD_TEST2_DIALOG };
#endif
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
// Implementation
protected:
HICON m_hIcon;
// Generated message map functions
virtual BOOL OnInitDialog();
afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
afx_msg void OnPaint();
afx_msg HCURSOR OnQueryDragIcon();
DECLARE_MESSAGE_MAP()
public:
afx_msg void OnNcPaint();
afx_msg LRESULT OnNcHitTest(CPoint point);
afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
afx_msg void OnLButtonUp(UINT nFlags, CPoint point);
};
Test2Dlg.cpp实现
// Test2Dlg.cpp : implementation file
//
#include "stdafx.h"
#include "Test2.h"
#include "Test2Dlg.h"
#include "afxdialogex.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
// CAboutDlg dialog used for App About
class CAboutDlg : public CDialogEx
{
public:
CAboutDlg();
// Dialog Data
#ifdef AFX_DESIGN_TIME
enum { IDD = IDD_ABOUTBOX };
#endif
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
// Implementation
protected:
DECLARE_MESSAGE_MAP()
};
CAboutDlg::CAboutDlg() : CDialogEx(IDD_ABOUTBOX)
{
}
void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
}
BEGIN_MESSAGE_MAP(CAboutDlg, CDialogEx)
END_MESSAGE_MAP()
// CTest2Dlg dialog
CTest2Dlg::CTest2Dlg(CWnd* pParent /*=NULL*/)
: CDialogEx(IDD_TEST2_DIALOG, pParent)
{
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}
void CTest2Dlg::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
}
BEGIN_MESSAGE_MAP(CTest2Dlg, CDialogEx)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_WM_NCPAINT()
ON_WM_NCHITTEST()
ON_WM_LBUTTONDOWN()
ON_WM_LBUTTONUP()
END_MESSAGE_MAP()
// CTest2Dlg message handlers
BOOL CTest2Dlg::OnInitDialog()
{
CDialogEx::OnInitDialog();
// Add "About..." menu item to system menu.
// IDM_ABOUTBOX must be in the system command range.
ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
ASSERT(IDM_ABOUTBOX < 0xF000);
CMenu* pSysMenu = GetSystemMenu(FALSE);
if (pSysMenu != NULL)
{
BOOL bNameValid;
CString strAboutMenu;
bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX);
ASSERT(bNameValid);
if (!strAboutMenu.IsEmpty())
{
pSysMenu->AppendMenu(MF_SEPARATOR);
pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
}
}
// Set the icon for this dialog. The framework does this automatically
// when the application's main window is not a dialog
SetIcon(m_hIcon, TRUE); // Set big icon
SetIcon(m_hIcon, FALSE); // Set small icon
// TODO: Add extra initialization here
return TRUE; // return TRUE unless you set the focus to a control
}
void CTest2Dlg::OnSysCommand(UINT nID, LPARAM lParam)
{
if ((nID & 0xFFF0) == IDM_ABOUTBOX)
{
CAboutDlg dlgAbout;
dlgAbout.DoModal();
}
else
{
CDialogEx::OnSysCommand(nID, lParam);
}
}
// If you add a minimize button to your dialog, you will need the code below
// to draw the icon. For MFC applications using the document/view model,
// this is automatically done for you by the framework.
void CTest2Dlg::OnPaint()
{
CPaintDC dc(this);
CRect rect;
CRect tmprect;
GetClientRect(&rect);
rect.bottom = 30;
tmprect = rect;
int nCount = 165 - 115 + 186 - 158 + 190 - 115;
int nIncrecs = (rect.right - rect.left) / nCount;
rect.left = rect.right = 0;
for(int i=115;i<=165;i++)
for(int j=158;j<=186;j++)
for(int k=115;k<=190;k++)
{
rect.left = rect.right;
rect.right += nIncrecs;
dc.FillSolidRect(rect, RGB(i, j, k));
}
// dc.FillSolidRect(rect, RGB(0, 0, 255));
dc.SetBkMode(TRANSPARENT);
CString str = TEXT("欢迎使用本软件");
dc.SetTextColor(RGB(255, 0, 0));
dc.DrawText(str, tmprect, DT_CENTER | DT_VCENTER | DT_SINGLELINE);//水平居中和垂直居中必须和DT_SINGLELINE搭配,否则起不到效果
//绘制关闭按钮
CRect rtBtnClo;
GetClientRect(&rtBtnClo);
rtBtnClo.left = rtBtnClo.right - 30;
rtBtnClo.right = rtBtnClo.right - 10;
rtBtnClo.top = 5;
rtBtnClo.bottom = 25;
m_rtBtnClose = rtBtnClo;
dc.Rectangle(rtBtnClo);
dc.MoveTo(rtBtnClo.left, rtBtnClo.top);
dc.LineTo(rtBtnClo.right, rtBtnClo.bottom);
dc.MoveTo(rtBtnClo.right, rtBtnClo.top);
dc.LineTo(rtBtnClo.left, rtBtnClo.bottom);
}
// The system calls this function to obtain the cursor to display while the user drags
// the minimized window.
HCURSOR CTest2Dlg::OnQueryDragIcon()
{
return static_cast<HCURSOR>(m_hIcon);
}
void CTest2Dlg::OnNcPaint()
{
// TODO: Add your message handler code here
// Do not call CDialogEx::OnNcPaint() for painting messages
}
LRESULT CTest2Dlg::OnNcHitTest(CPoint point)
{
// TODO: Add your message handler code here and/or call default
UINT nHitTest=CDialogEx::OnNcHitTest(point);
CRect rect;
GetClientRect(&rect);
rect.bottom = 30;
//函数参数point是相对于屏幕坐标的,需要将其转换为
//客户区坐标才能使用PtInRect(),否则会因为坐标的不同使判断失误
ScreenToClient(&point);
if (rect.PtInRect(point))
{
if (HTCLIENT == nHitTest)
nHitTest = HTCAPTION;
//如果鼠标点中的是关闭按钮的位置,需要将上一步的设置还原,
//否则鼠标点击自绘的标题栏的时候,系统无法发送WM_LBUTTONDOWN消息,也就无法处理关闭按钮
if (m_rtBtnClose.PtInRect(point))
{
nHitTest = HTCLIENT;
}
}
return nHitTest;
}
void CTest2Dlg::OnLButtonDown(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
// ScreenToClient(&point);
if (m_rtBtnClose.PtInRect(point))
{
//AfxMessageBox(TEXT("测试关闭按钮"));
CDialog::OnCancel();
}
CDialogEx::OnLButtonDown(nFlags, point);
}
void CTest2Dlg::OnLButtonUp(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
/*ScreenToClient(&point);
if (m_rtBtnClose.PtInRect(point))
{
AfxMessageBox(TEXT("测试关闭按钮"));
}*/
CDialogEx::OnLButtonUp(nFlags, point);
}