MFC自绘Button按钮分析和实现

 更新时间:2019年02月20日 15:41:59   作者:Jimmy1224  
这篇文章主要为大家详细介绍了MFC自绘Button按钮分析和实现,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

对于按压式的Button按钮,WM_CTLCOLOR无法完成按钮控件的背景颜色和字体颜色修改,若想要完成外观定制需要添加WM_DRAWITEM消息或者重写CButton类中的DrawItem函数完成按钮外观修改,下面逐一介绍。

自绘制知识基础

自绘制需要的两个基础步骤:

1.控件类型设置为 BS_OWNERDRAW

2.添加WM_DRAWITEM消息响应函数或者重写DrawItem函数。

WM_DRAWITEM消息响应原型如下:

afx_msg void OnDrawItem( int nIDCtl, LPDRAWITEMSTRUCT lpDrawItemStruct );

参数:

nIDCtl

包含了发送WM_DRAWITEM消息的控件的标识符。如果菜单发送了此消息,则nIDCtl中包含0。

lpDrawItemStruct

指定了指向DRAWITEMSTRUCT数据结构的长指针,其中包含有关要画出的项和要求的绘图类型的信息。

说明:

当控件或菜单的可视状态发生变化时,框架为自画按钮控件、组合框控件、列表框控件或者菜单的拥有者调用这个成员函数。

DRAWITEMSTRUCT结构体信息如下:

typedef struct tagDRAWITEMSTRUCT 
{
 UINT CtlType; 
 UINT CtlID; 
 UINT itemID; 
 UINT itemAction; 
 UINT itemState; 
 HWND hwndItem; 
 HDC hDC; 
 RECT rcItem; 
 ULONG_PTR itemData; 
} DRAWITEMSTRUCT; 

DrawItem函数原型如下,入参和OnDrawItem一样,不再重复介绍:

virtual void DrawItem(LPDRAWITEMSTRUCT /*lpDrawItemStruct*/);

需要说明的是,若某个控件拥有了BS_OWNERDRAW属性,则控件的绘制将由父窗口完成转移给了“程序员”,程序员需要完成所有的绘制工作,也就实现了控件自绘,若没有完成控件绘制功作,我们在界面上将看不到对应的控件,我们在重绘某个控件时,强烈推荐使用子类化方法,比如想自绘Button控件, 首先添加自己的类CMYButton 继承自 CButton,这样的设计符合程序设计思想,避免了父类功能的臃肿,也实现了程序的复用。

自绘制原理分析

自绘基本流程我已经绘制成流程图了,这样也清晰易懂,图中的数字标识也给出了简单介绍,自绘流程如图1所示:

图1 自绘制原理

1.子控件将要被绘制时,检测该控件有BS_OWNERDRAW属性,则向父窗口发送WM_DRAWITEM消息
2.父窗口完成收集和准备控件绘制所必要的信息
3.若ReflectLastMsg函数返回TRUE,表示消息反射成功,使用子控件中的绘制代码
4.若ReflectLastMsg函数返回FALSE,表示消息反射失败,使用父窗口中的绘制代码
5.消息反射成功了,我们就可以重写CButton中DrawItem虚函数完成控件自绘制

下面将展示如何完成控件的绘制步骤:

1.基于对话框建立DrawButton工程

2.添三个按压式Button控件,其ID分别四IDC_YES,IDC_NO, IDC_HELP如图2所示:

图2  控件布局

3.分别在父窗口(对话框)和子类化中实现自绘,具体过程如下:

父窗口中实现自绘制

具体过程:类视图->属性->查找WM_WM_DRAWITEM->添加并编辑"OnDrawItem",其代码如下:

void CDrawButtonDlg::OnDrawItem(int nIDCtl, LPDRAWITEMSTRUCT lpDrawItemStruct)
{
 // TODO: 在此添加消息处理程序代码和/或调用默认值
 
 //获得button标题 
 CString btnCaption = _T("Dialog");
 CDC* pDC = CDC::FromHandle(lpDrawItemStruct->hDC);
 
 CRect drawRect;
 //获得绘图DC
 //得到原Button的矩形大小
 drawRect.CopyRect(&(lpDrawItemStruct->rcItem)); 
 //绘制控件框架 
 pDC->DrawFrameControl(&drawRect,DFC_BUTTON,lpDrawItemStruct->CtlType);
 
 
 //创建画刷
 CBrush pBrush;
 pBrush.CreateSolidBrush(RGB(100,130,10));
 //画矩形 
 pDC->FillRect(drawRect,&pBrush);
 
 
 //定义一个CRect用于绘制文本 
 CRect textRect;
 //拷贝矩形区域 
 textRect.CopyRect(&drawRect);
 //获得字符串尺寸
 CSize sz = pDC->GetTextExtent(btnCaption);
 //调整文本位置 居中 
 textRect.top += (textRect.Height()- sz.cy)/2;
 //设置文本背景透明 
 pDC->SetBkMode(TRANSPARENT);
 //设置文本颜色
 pDC->SetTextColor(RGB(0,0,255)); 
 //绘制文本内容
 pDC->DrawText(btnCaption,&textRect,DT_RIGHT|DT_CENTER|DT_BOTTOM);
 
 CDialog::OnDrawItem(nIDCtl, lpDrawItemStruct);
}

子类化实现自绘制

具体过程:

1.类视图->点击工程名->点击添加->MFC类->基类:CButton,子类命名为CMyButton。

2.资源视图->控件IDC_YES,IDC_HELP添加变量->类型选择为CMyButton。

在自定义的CMyButton中我们添加DrawItem虚函数,其具体代码如下:

void CMyButton::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
{
 CString btnCaption = ""; 
 //设置标题
 switch (lpDrawItemStruct->CtlID)
 {
 case IDC_YES:
 btnCaption = "Yes";
 break;
 case IDC_NO:
 btnCaption = "No";
 break;
 case IDC_HELP:
 btnCaption = "Help";
 break;
 default:
 ;
 } 
 
 CDC* pDC = CDC::FromHandle(lpDrawItemStruct->hDC);
 
 CRect drawRect;
 //获得绘图DC
 //得到原Button的矩形大小
 drawRect.CopyRect(&(lpDrawItemStruct->rcItem)); 
 //绘制控件框架 
 pDC->DrawFrameControl(&drawRect,DFC_BUTTON,lpDrawItemStruct->CtlType);
 
 //使得初始的button颜色不一样
 static int nStepColor = 0; 
 
 //创建画刷
 CBrush pBrush;
 pBrush.CreateSolidBrush(RGB(100+nStepColor,130,nStepColor));
 //画矩形 
 pDC->FillRect(drawRect,&pBrush);
 
 
 //定义一个CRect用于绘制文本 
 CRect textRect;
 //拷贝矩形区域 
 textRect.CopyRect(&drawRect);
 //获得字符串尺寸
 CSize sz = pDC->GetTextExtent(btnCaption);
 //调整文本位置 居中 
 textRect.top += (textRect.Height()- sz.cy)/2;
 //设置文本背景透明 
 pDC->SetBkMode(TRANSPARENT);
 //设置文本颜色
 pDC->SetTextColor(RGB(0,0,255)); 
 //绘制文本内容
 pDC->DrawText(btnCaption,&textRect,DT_RIGHT|DT_CENTER|DT_BOTTOM);
 nStepColor += 120;
 
}

运行效果:

说明中间一个Button按钮的自绘是由父窗口的OnDrawItem实现的。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

您可能感兴趣的文章:

相关文章

  • C语言实现静态链表的方法

    C语言实现静态链表的方法

    分享一段代码,一个静态链表的C语言实现,其中包含着一种简单的内存管理策略:固定大小的链式管理。
    2013-03-03
  • C++用read()和write()读写二进制文件的超详细教程

    C++用read()和write()读写二进制文件的超详细教程

    二进制的文件肉眼我们是读不懂的,如果通过二进制的读写操作就可以读懂,下面这篇文章主要给大家介绍了关于C++用read()和write()读写二进制文件的相关资料,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
    2022-06-06
  • Windows下Qt读取系统的内存、CPU、GPU等使用信息的示例代码

    Windows下Qt读取系统的内存、CPU、GPU等使用信息的示例代码

    在当今计算机应用广泛的领域中,了解系统的内存、CPU和GPU使用情况是非常重要的,本文将介绍如何使用Qt和Windows API来读取系统的内存、CPU和GPU使用详细信息,将提供一个完整的示例代码,需要的朋友可以参考下
    2024-01-01
  • VSstudio中scanf返回值被忽略的原因及解决方法(推荐)

    VSstudio中scanf返回值被忽略的原因及解决方法(推荐)

    这篇文章主要介绍了VSstudio中scanf返回值被忽略的原因及其解决方法,scanf返回值被忽略,接下来我就告诉大家该如何解决这个问题,需要的朋友可以参考下
    2022-09-09
  • Qt简单实现密码器控件

    Qt简单实现密码器控件

    这篇文章主要为大家详细介绍了Qt简单实现密码器控件,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-06-06
  • Qt禁止程序多开的实现示例

    Qt禁止程序多开的实现示例

    本文主要介绍了Qt 禁止程序多开的实现示例,主要介绍了三种方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-09-09
  • C语言rewind与fseek函数之随机读写文件的用法详解

    C语言rewind与fseek函数之随机读写文件的用法详解

    这篇文章主要介绍了C语言rewind与fseek函数之随机读写文件的用法详解,本篇文章通过简要的案例,讲解了该项技术的了解与使用,以下就是详细内容,需要的朋友可以参考下
    2021-09-09
  • C语言数据结构之单链表存储详解

    C语言数据结构之单链表存储详解

    链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。本文将和大家一起聊聊C语言中单链表的存储,感兴趣的可以学习一下
    2022-07-07
  • C语言全面梳理结构体知识点

    C语言全面梳理结构体知识点

    结构体是一些值的集合,这些值称为成员变量,结构体的每个成员可以是不同类型的变量。本文将通过示例为大家详细讲讲C语言中结构体的使用,需要的可以参考一下
    2022-07-07
  • C++实现LeetCode(889.由先序和后序遍历建立二叉树)

    C++实现LeetCode(889.由先序和后序遍历建立二叉树)

    这篇文章主要介绍了C++实现LeetCode(889.由先序和后序遍历建立二叉树),本篇文章通过简要的案例,讲解了该项技术的了解与使用,以下就是详细内容,需要的朋友可以参考下
    2021-07-07

最新评论