MFC自定义控件开发与使用指南
MFC自定义控件开发与使用指南
1. 概述
MFC(Microsoft Foundation Classes)框架提供了丰富的内置控件,但在实际开发中,我们常常需要创建自定义控件来满足特定的界面需求。本文将详细介绍如何在MFC中开发自定义控件,并以CCustomTextControl
为例,展示自定义控件的实现和使用方法。
示例代码仓库:https://github.com/wang161113/MFCSplitWindow
2. 自定义控件的基本原理
在MFC中,创建自定义控件通常有以下几种方式:
- 继承自现有控件类:如
CButton
、CEdit
等,适合对现有控件进行功能扩展。 - 继承自
CWnd
类:完全自定义控件的外观和行为,拥有最大的灵活性。 - 使用
CStatic
控件的owner-draw
特性:通过重写绘制函数来自定义静态控件的外观。
本文将重点介绍第二种方式,即通过继承CWnd
类来创建完全自定义的控件。
3. CCustomTextControl实现分析
3.1 类定义
首先,我们来看CCustomTextControl
的类定义:
class CCustomTextControl : public CWnd {
DECLARE_DYNAMIC(CCustomTextControl)
public:
CCustomTextControl();
virtual ~CCustomTextControl();
// 接口方法
void SetUpperText(LPCTSTR text);
void SetLowerText(LPCTSTR text);
void SetUpperFont(CFont* pFont);
void SetLowerFont(CFont* pFont);
void SetUpperColor(COLORREF color);
void SetLowerColor(COLORREF color);
void SetBkColor(COLORREF color);
protected:
DECLARE_MESSAGE_MAP()
afx_msg void OnPaint();
afx_msg BOOL OnEraseBkgnd(CDC* pDC);
private:
CString m_upperText;
CString m_lowerText;
CFont* m_pUpperFont;
CFont* m_pLowerFont;
COLORREF m_upperColor;
COLORREF m_lowerColor;
COLORREF m_bkColor;
CBitmap m_memBitmap;
virtual BOOL PreCreateWindow(CREATESTRUCT& cs);
};
这个类继承自CWnd
,包含以下关键部分:
- 成员变量:存储上下文本、字体、颜色等属性
- 公共接口:提供设置文本、字体和颜色的方法
- 消息处理:处理绘制和背景擦除消息
- 窗口创建:通过
PreCreateWindow
自定义窗口类样式
3.2 类实现
3.2.1 构造函数和析构函数
CCustomTextControl::CCustomTextControl()
{
m_upperColor = RGB(0, 0, 0);
m_lowerColor = RGB(0, 0, 0);
m_bkColor = RGB(255, 255, 255);
m_pUpperFont = nullptr;
m_pLowerFont = nullptr;
}
CCustomTextControl::~CCustomTextControl()
{
}
构造函数初始化成员变量,设置默认颜色和字体指针。
3.2.2 消息映射
BEGIN_MESSAGE_MAP(CCustomTextControl, CWnd)
ON_WM_PAINT()
ON_WM_ERASEBKGND()
END_MESSAGE_MAP()
消息映射将WM_PAINT
和WM_ERASEBKGND
消息映射到对应的处理函数。
3.2.3 绘制函数
void CCustomTextControl::OnPaint()
{
CPaintDC dc(this);
CRect rect;
GetClientRect(&rect);
// 双缓冲绘制
CDC memDC;
memDC.CreateCompatibleDC(&dc);
m_memBitmap.DeleteObject();
m_memBitmap.CreateCompatibleBitmap(&dc, rect.Width(), rect.Height());
CBitmap* pOldBitmap = memDC.SelectObject(&m_memBitmap);
// 绘制背景
memDC.FillSolidRect(rect, m_bkColor);
// 预定义分割区域
CRect upperRect(rect);
upperRect.left = 15; //左边距,DT_CENTER时可设置为0
upperRect.bottom = upperRect.top + rect.Height() / 2;
CRect lowerRect(rect);
lowerRect.left = 15; //左边距,DT_CENTER时可设置为0
lowerRect.top = upperRect.bottom;
// 绘制上层文本
if (!m_upperText.IsEmpty())
{
memDC.SetTextColor(m_upperColor);
if (m_pUpperFont)
memDC.SelectObject(m_pUpperFont);
//DT_VCENTER垂直居中;DT_LEFT左对齐,换成DT_CENTER可变成水平居中
memDC.DrawText(m_upperText, upperRect, DT_VCENTER | DT_LEFT | DT_SINGLELINE);
}
// 绘制下层文本
if (!m_lowerText.IsEmpty())
{
memDC.SetTextColor(m_lowerColor);
if (m_pLowerFont)
memDC.SelectObject(m_pLowerFont);
memDC.DrawText(m_lowerText, lowerRect, DT_VCENTER | DT_LEFT | DT_SINGLELINE);
}
// 拷贝到屏幕DC
dc.BitBlt(0, 0, rect.Width(), rect.Height(), &memDC, 0, 0, SRCCOPY);
memDC.SelectObject(pOldBitmap);
}
OnPaint
函数是自定义控件的核心,它实现了控件的绘制逻辑:
- 创建内存DC进行双缓冲绘制,避免闪烁
- 绘制背景色
- 将控件区域分为上下两部分
- 分别在上下区域绘制文本,应用相应的字体和颜色
- 将内存DC的内容复制到屏幕DC
3.2.4 属性设置方法
void CCustomTextControl::SetUpperText(LPCTSTR text)
{
m_upperText = text;
if(GetSafeHwnd()) Invalidate();
}
// 其他属性设置方法类似...
每个属性设置方法都会在修改属性后调用Invalidate()
触发重绘,确保界面及时更新。
3.2.5 窗口创建前的准备
BOOL CCustomTextControl::PreCreateWindow(CREATESTRUCT& cs)
{
cs.lpszClass = AfxRegisterWndClass(CS_HREDRAW | CS_VREDRAW);
return CWnd::PreCreateWindow(cs);
}
PreCreateWindow
函数注册了一个具有CS_HREDRAW
和CS_VREDRAW
样式的窗口类,这确保了控件在大小改变时会完全重绘。
4. 在对话框中使用CCustomTextControl
4.1 在对话框类中声明控件变量
在CLeftDlg.h
中:
// CLeftDlg 对话框
class CLeftDlg : public CDialogEx
{
// ...
protected:
CCustomTextControl m_customTextControl;
// ...
public:
CFont m_upperFont;
CFont m_lowerFont;
// ...
};
4.2 创建和初始化控件
在CLeftDlg.cpp
的OnInitDialog
函数中:
BOOL CLeftDlg::OnInitDialog()
{
CDialogEx::OnInitDialog();
// 创建自定义文本控件
CRect rcPlaceholder;
GetDlgItem(IDC_CUSTOM_TEXT)->GetWindowRect(&rcPlaceholder);
ScreenToClient(&rcPlaceholder);
if (!m_customTextControl.Create(_T("CCustomTextControl"), L"", WS_CHILD | WS_VISIBLE, rcPlaceholder, this, IDC_CUSTOM_TEXT))
TRACE("Failed to create custom text control!\n");
//初始化 设置文本、字体、颜色
m_upperFont.CreatePointFont(120, _T("Arial"));
m_lowerFont.CreatePointFont(90, _T("Arial"));
m_customTextControl.SetUpperText(_T("Upper Text"));
m_customTextControl.SetLowerText(_T("Lower Text"));
m_customTextControl.SetBkColor(RGB(0, 0, 0));
m_customTextControl.SetUpperFont(&m_upperFont);
m_customTextControl.SetLowerFont(&m_lowerFont);
m_customTextControl.SetUpperColor(RGB(0, 0, 255));
m_customTextControl.SetLowerColor(RGB(128, 0, 0));
return TRUE;
}
这段代码展示了如何创建和初始化自定义控件:
- 获取占位控件的位置(通常在对话框模板中放置一个静态控件作为占位符)
- 调用
Create
方法创建控件实例 - 创建字体对象
- 设置控件的文本、字体和颜色属性
4.3 动态更新控件属性
在CLeftDlg.cpp
的OnBnClickedButton1
函数中:
void CLeftDlg::OnBnClickedButton1()
{
// 创建随机数生成器
srand(static_cast<unsigned int>(time(nullptr)));
// 生成随机颜色的辅助函数
auto GetRandomColor = []() {
return RGB(rand() % 256, rand() % 256, rand() % 256);
};
int upperFontSize = 80 + (rand() % 121); // 80-200之间的随机数
int lowerFontSize = 80 + (rand() % 121); // 80-200之间的随机数
// 释放旧字体
if (m_upperFont.GetSafeHandle())
m_upperFont.DeleteObject();
if (m_lowerFont.GetSafeHandle())
m_lowerFont.DeleteObject();
m_upperFont.CreatePointFont(upperFontSize, _T("Arial"));
m_lowerFont.CreatePointFont(lowerFontSize, _T("Arial"));
m_customTextControl.SetUpperFont(&m_upperFont);
m_customTextControl.SetLowerFont(&m_lowerFont);
static int i = 1;
m_customTextControl.SetUpperText(_T("Upper Text ") + CString(std::to_string(i++).c_str()));
m_customTextControl.SetUpperColor(GetRandomColor()); // 为上文本设置随机颜色
m_customTextControl.SetLowerText(_T("Lower Text ") + CString(std::to_string(rand()).c_str()));
m_customTextControl.SetLowerColor(GetRandomColor()); // 为下文本设置随机颜色
m_customTextControl.SetBkColor(GetRandomColor()); // 背景也设置为随机颜色
}
这段代码展示了如何动态更新控件属性:
- 生成随机字体大小和颜色
- 释放旧字体资源并创建新字体
- 更新控件的文本、字体和颜色属性
5. 自定义控件开发的最佳实践
5.1 设计原则
- 单一职责原则:控件应专注于一个特定功能
- 接口清晰:提供简洁明了的公共接口
- 资源管理:正确管理GDI资源,避免泄漏
- 性能优化:使用双缓冲等技术避免闪烁
5.2 常见问题及解决方案
- 控件闪烁:使用双缓冲绘制技术
- 资源泄漏:确保GDI对象(字体、画笔等)正确释放
- 控件响应性:在属性变更后调用
Invalidate()
触发重绘 - 字体管理:注意字体对象的生命周期管理
6. 总结
通过本文的介绍,我们学习了如何在MFC中创建自定义控件,并通过CCustomTextControl
的实例详细了解了自定义控件的实现和使用方法。自定义控件为MFC应用程序提供了更大的灵活性和更丰富的用户界面,是MFC开发中的重要技术。
开发自定义控件的关键步骤包括:
- 继承适当的基类(通常是
CWnd
) - 实现消息映射和处理函数
- 提供清晰的公共接口
- 在对话框或视图中创建和使用控件
通过掌握这些技术,您可以根据应用程序的需求创建各种自定义控件,提升用户体验。

