MFC中要实现自定义按钮,首先要创建一个类并继承自CButton。我这里创建的类名为CMainButton
class CMainButton : public CButton
{
DECLARE_DYNAMIC(CMainButton)
public:
CMainButton(UINT nID,CRect rcWnd,CWnd* pParent=nullptr);//nID为按钮ID,rcWnd为按钮位置
virtual ~CMainButton();
void SetInvalid(); //调用了_SetInvalidPt(m_clickPoint)
protected:
bool IsValidPt(const CPoint& pt) const; //pt是否有效,当pt的x和y都大于等于0时,程序认为有效。
// 主要为m_clickPoint服务,因为是在客户区点击,故不可能点击到x小于0或y小于0的位置
// 返回true表示之前点击了鼠标左键
int GetFontPtSize() const;//自定义实现的按钮默认可以调整大小,根据按钮大小返回自适应内容字体大小
void _SetInvalidPt(CPoint& pt); //让pt无效
CString m_strOne; //单态(m_bOne为true)下,显示的窗口内容
/// <summary>
/// 3-triState
/// </summary>
CString m_strLeft; //三态(m_bOne为false)下,最左边显示的窗口内容
CString m_strCenter; //三态(m_bOne为false)下,中间显示的窗口内容
CString m_strRight; //三态(m_bOne为false)下,最右边显示的窗口内容
BOOL m_bOne; //m_bOne为true,表示单态,为false,表示多态
CRect m_rcLeft; //三态(m_bOne为false)下,最左边显示的窗口区域
CRect m_rcCenter;//三态(m_bOne为false)下,中间显示的窗口区域
CRect m_rcRight;//三态(m_bOne为false)下,最右边显示的窗口区域
protected:
DECLARE_MESSAGE_MAP()
public:
virtual void DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct);
private:
CPoint m_clickPoint; // 鼠标点击位置
public:
afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
};
要实现自绘,还需要在调用Create时传窗口风格,传递的风格中要加上BS_OWNERDRAW风格,如果没有BS_OWNERDRAW风格,窗口不会调用DrawItem实现程序员的自绘操作。在我实现的控件中,将类与窗口绑定,类创建时窗口也创建了,类消亡时窗口也销毁了。实现如下:
CMainButton::CMainButton(UINT nID, CRect rcWnd, CWnd* pParent)
{
m_strOne = _T("选择");
m_strLeft = _T("分割文件");
m_strCenter = _T("取消");
m_strRight = _T("合并文件");
DWORD dwStyle =WS_VISIBLE | BS_PUSHBUTTON | BS_OWNERDRAW;
if (pParent){
dwStyle |= WS_CHILD;
}
Create(m_strOne, dwStyle, rcWnd, pParent, nID);
m_bOne = TRUE;
SetInvalid();
}
CMainButton::~CMainButton()
{
DestroyWindow();
}
加上了这个风格后,下面我们来看看自绘操作DrawItem
void CMainButton::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
{
CDC* pDC = CDC::FromHandle(lpDrawItemStruct->hDC); // 获取设备上下文
RECT rect = lpDrawItemStruct->rcItem; // 获取绘制区域
UINT state = lpDrawItemStruct->itemState; // 获取按钮状态
CFont wndFont;
int ptFontSize = GetFontPtSize(); //根据窗口大小计算窗口内容文字大小
wndFont.CreatePointFont(ptFontSize, _T("微软雅黑"));
CFont* pOldFont = pDC->SelectObject(&wndFont);
COLORREF buttonColor = GetSysColor(COLOR_BTNFACE); //获取按钮背景色
if (m_bOne){//单态按钮状态下的绘制
if (state & ODS_SELECTED) {
pDC->DrawText(m_strOne, &rect, DT_CENTER | DT_VCENTER | DT_SINGLELINE); // 绘制文本
pDC->DrawEdge(&rect, EDGE_SUNKEN, BF_RECT); // 按钮按下时的边框
if (IsValidPt(m_clickPoint)){
m_bOne = FALSE;
}
}
else {
pDC->FillSolidRect(&rect, buttonColor);
pDC->DrawText(m_strOne, &rect, DT_CENTER | DT_VCENTER | DT_SINGLELINE); // 绘制文本
pDC->DrawEdge(&rect, EDGE_RAISED, BF_RECT); // 按钮未按下时的边框
}
}
else {
int width = rect.right - rect.left;
int perWidth = width / 3;
if (state & ODS_SELECTED) {
if (m_rcCenter.PtInRect(m_clickPoint)) {
m_bOne = TRUE;
}
if (IsValidPt(m_clickPoint)) { //说明经过了LButtonDown,左键单机了
for (int i = 0; i < 3; i++) {
CRect rc(rect.left + perWidth *i, rect.top, rect.left + perWidth * (i + 1), rect.bottom);
if (i == 0) {
pDC->DrawText(m_strLeft, &rc, DT_CENTER | DT_VCENTER | DT_SINGLELINE); // 绘制文本
}
else if (i == 1) {
pDC->DrawText(m_strCenter, &rc, DT_CENTER | DT_VCENTER | DT_SINGLELINE); // 绘制文本
}
else if (i == 2) {
pDC->DrawText(m_strRight, &rc, DT_CENTER | DT_VCENTER | DT_SINGLELINE); // 绘制文本
}
if (rc.PtInRect(m_clickPoint)) {
pDC->DrawEdge(&rc, EDGE_SUNKEN, BF_RECT); // 按钮按下时的边框
_SetInvalidPt(m_clickPoint);
}
}
}
}
else {
for (int i = 0; i < 3; i++) {
CRect rc(rect.left + perWidth * i, rect.top, rect.left + perWidth * (i + 1), rect.bottom);
if (i==0){
pDC->DrawText(m_strLeft, &rc, DT_CENTER | DT_VCENTER | DT_SINGLELINE); // 绘制文本
m_rcLeft = rc;
}
else if (i == 1) {
pDC->DrawText(m_strCenter, &rc, DT_CENTER | DT_VCENTER | DT_SINGLELINE); // 绘制文本
m_rcCenter = rc;
}
else if (i == 2) {
pDC->DrawText(m_strRight, &rc, DT_CENTER | DT_VCENTER | DT_SINGLELINE); // 绘制文本
m_rcRight = rc;
}
pDC->DrawEdge(&rc, EDGE_RAISED, BF_RECT); // 按钮未按下时的边框
}
}
}
}
标签:MFC,自定义,rc,bOne,按钮,DT,pDC,rect
From: https://www.cnblogs.com/dxy-blog/p/18361519