欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 文旅 > 明星 > MFC 自定义编辑框:打造灵活的数据输入控件

MFC 自定义编辑框:打造灵活的数据输入控件

2025/6/26 0:20:40 来源:https://blog.csdn.net/m0_58648890/article/details/144401530  浏览:    关键词:MFC 自定义编辑框:打造灵活的数据输入控件

在用户界面开发中,输入框是最基础的控件之一。然而,在许多场景下,我们希望限制用户的输入格式,例如只能输入数字、小数、字母等。MFC 提供了强大的自定义控件机制,结合正则表达式,可以实现灵活的数据输入校验。本文将介绍如何通过自定义编辑框与正则表达式的结合,打造灵活高效的输入控件。

什么是正则表达式?

正则表达式(Regular Expression, regex) 是一种描述字符模式的语言。它用于字符串的查找、匹配和替换操作。通过正则表达式,可以轻松定义复杂的文本格式校验规则。

常用的正则表达式语法

符号说明
.匹配任意字符(除换行符外)
*匹配前一个字符 0 次或多次
+匹配前一个字符 1 次或多次
?匹配前一个字符 0 次或 1 次
[abc]匹配方括号中的任意一个字符
[^abc]匹配不在方括号中的字符
\d匹配数字(等价于 [0-9]
\w匹配字母、数字或下划线
\s匹配空白字符(空格、制表符等)
^匹配字符串开头
$匹配字符串结尾
()表示捕获分组
``

示例正则表达式

表达式描述
^\d+$只包含数字,且至少一个字符
^\d{3}$必须是 3 位数字
^[a-zA-Z]+$只包含大小写字母
^\w+@\w+\.\w+$简单的邮箱格式验证
^\d+(\.\d{1,2})?$数字,可以有 1-2 位小数

自定义编辑框与正则表达式结合

在 MFC 中,自定义控件可以通过派生子类实现。我们设计了一个 CRegexEdit 类,用于结合正则表达式进行输入校验。它支持以下功能:

  • 限制输入的格式,例如只能输入数字、字母、小数等。
  • 提供自定义比较函数,可以实现范围校验等功能。
  • 当输入不合法时,支持触发回调函数,提示用户。

以下是 CRegexEdit 的实现核心代码。

实现编辑框的校验逻辑

OnChar 中实现字符输入校验逻辑。

void CRegexEdit::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags)
{// 处理删除键和退格键if (nChar == VK_BACK || nChar == VK_DELETE) {CEdit::OnChar(nChar, nRepCnt, nFlags);return;}CString strCurrent;GetWindowText(strCurrent);  // 获取当前文本// 获取光标位置int nStartChar, nEndChar;GetSel(nStartChar, nEndChar);std::string strText(CT2A(strCurrent.GetBuffer()));strText.insert(strText.begin() + nStartChar, (char)nChar);// 校验规则:正则表达式 + 范围校验bool bValid = m_customComparator ? m_customComparator(strText) : IsValueInRange(strText);if (std::regex_match(strText, GetCurrentRegex()) && bValid) {CEdit::OnChar(nChar, nRepCnt, nFlags);}else {// 输入错误处理if (m_invalidInputCallback) {m_invalidInputCallback();}}
}

配置校验规则

实现 SetRegexTypeGetCurrentRegex 函数。

void CRegexEdit::SetRegexType(RegexType enType)
{m_enRegexType = enType;
}std::regex CRegexEdit::GetCurrentRegex() const
{switch (m_enRegexType){case RegexType::Alphanumeric:return std::regex("^[a-zA-Z0-9]*$");        // 字母和数字case RegexType::Letters:return std::regex("^[a-zA-Z]*$");           // 只允许字母case RegexType::Digits:return std::regex("^\\d*$");                // 只允许数字case RegexType::Decimal:return std::regex("^[-+]?\\d+(\\.\\d+)?$"); // 允许小数和整数case RegexType::Custom:return std::regex(m_strCustomRegex);        // 自定义正则default:return std::regex(".*");                    // 默认允许任何内容}
}

支持查找和替换

支持用户查找和替换编辑框中的内容。

bool CRegexEdit::FindMatch(const std::string& pattern, std::string& foundText)
{CString currentText;GetWindowText(currentText);std::string text(CT2A(currentText.GetString()));std::regex regexPattern(pattern);std::smatch match;if (std::regex_search(text, match, regexPattern)) {foundText = match.str();return true;}return false;
}void CRegexEdit::ReplaceMatch(const std::string& pattern, const std::string& replacement)
{CString currentText;GetWindowText(currentText);std::string text(CT2A(currentText.GetString()));std::regex regexPattern(pattern);std::string result = std::regex_replace(text, regexPattern, replacement);SetWindowText(CString(result.c_str()));
}

类声明

#if !defined(AFX_REGEXEDIT_H__A4EABEC5_2E8C_11D1_B79F_00805F9ECE10__INCLUDED_)
#define AFX_REGEXEDIT_H__A4EABEC5_2E8C_11D1_B79F_00805F9ECE10__INCLUDED_#if _MSC_VER >= 1000
#pragma once
#endif // _MSC_VER >= 1000#include <afxwin.h>
#include <regex>
#include <functional>
#include <limits>// 枚举类型:正则表达式类型
enum class RegexType
{Alphanumeric,   // 允许字母和数字Letters,        // 只允许字母Digits,         // 只允许数字Decimal,        // 允许小数和整数Custom          // 自定义正则
};class CRegexEdit : public CEdit
{DECLARE_DYNAMIC(CRegexEdit)public:// 构造与析构CRegexEdit();virtual ~CRegexEdit();// 设置正则类型void SetRegexType(RegexType enType);// 设置自定义正则表达式void SetCustomRegex(const std::string& strCustomRegex);// 设置数值范围(整数或浮点)void SetValueRange(long double dMinValue, long double dMaxValue);// 设置自定义比较函数void SetCustomComparator(std::function<bool(const std::string&)> comparator);// 设置输入不合法函数void SetInvalidInputCallback(std::function<void()> callback);// 查找匹配内容bool FindMatch(const std::string& pattern, std::string& foundText);// 替换匹配内容void ReplaceMatch(const std::string& pattern, const std::string& replacement);protected:// 根据枚举值返回对应的正则表达式std::regex GetCurrentRegex() const;// 校验输入是否在指定范围内bool IsValueInRange(const std::string& strText);protected:RegexType m_enRegexType;        // 当前选中的正则类型std::string m_strCustomRegex;   // 自定义正则表达式long double m_dMinValue;        // 最小值long double m_dMaxValue;		// 最大值std::function<bool(const std::string&)> m_customComparator; // 自定义比较函数std::function<void()> m_invalidInputCallback;               // 不合法输入的回调函数protected:void OnChar(UINT nChar, UINT nRepCnt, UINT nFlags);DECLARE_MESSAGE_MAP()
};#endif // !defined(AFX_REGEXEDIT_H__A4EABEC5_2E8C_11D1_B79F_00805F9ECE10__INCLUDED_)

类实现

#include "stdafx.h"
#include "RegexEdit.h"
#include <stdexcept>IMPLEMENT_DYNAMIC(CRegexEdit, CEdit)CRegexEdit::CRegexEdit()
{m_enRegexType = RegexType::Alphanumeric;m_dMinValue = LDBL_MIN;m_dMaxValue = LDBL_MAX;
}CRegexEdit::~CRegexEdit()
{
}void CRegexEdit::SetRegexType(RegexType enType)
{m_enRegexType = enType;
}void CRegexEdit::SetCustomRegex(const std::string& strCustomRegex)
{m_strCustomRegex = strCustomRegex;
}void CRegexEdit::SetValueRange(long double dMinValue, long double dMaxValue)
{m_dMinValue = dMinValue;m_dMaxValue = dMaxValue;
}void CRegexEdit::SetCustomComparator(std::function<bool(const std::string&)> comparator)
{m_customComparator = comparator;
}void CRegexEdit::SetInvalidInputCallback(std::function<void()> callback)
{m_invalidInputCallback = callback;
}bool CRegexEdit::FindMatch(const std::string& pattern, std::string& foundText)
{CString currentText;GetWindowText(currentText);std::string text(CT2A(currentText.GetString()));std::regex regexPattern(pattern);std::smatch match;if (std::regex_search(text, match, regexPattern)) {foundText = match.str();return true;}return false;
}void CRegexEdit::ReplaceMatch(const std::string& pattern, const std::string& replacement)
{CString currentText;GetWindowText(currentText);std::string text(CT2A(currentText.GetString()));std::regex regexPattern(pattern);std::string result = std::regex_replace(text, regexPattern, replacement);SetWindowText(CString(result.c_str()));
}std::regex CRegexEdit::GetCurrentRegex() const
{switch (m_enRegexType){case RegexType::Alphanumeric:return std::regex("^[a-zA-Z0-9]*$");        // 字母和数字case RegexType::Letters:return std::regex("^[a-zA-Z]*$");           // 只允许字母case RegexType::Digits:return std::regex("^\\d*$");                // 只允许数字case RegexType::Decimal:return std::regex("^[-+]?\\d+(\\.\\d+)?$"); // 允许小数和整数case RegexType::Custom:return std::regex(m_strCustomRegex);        // 自定义正则default:return std::regex(".*");                    // 默认允许输入任何内容}
}bool CRegexEdit::IsValueInRange(const std::string& strText)
{try {if (m_enRegexType == RegexType::Digits || m_enRegexType == RegexType::Decimal) {if (strText.find('.') == std::string::npos) {int nValue = std::stoi(strText);return nValue >= static_cast<int>(m_dMinValue) && nValue <= static_cast<int>(m_dMaxValue);}else {double dValue = std::stod(strText);return dValue >= m_dMinValue && dValue <= m_dMaxValue;}}}catch (const std::invalid_argument&) {return false;}return true;
}BEGIN_MESSAGE_MAP(CRegexEdit, CEdit)ON_WM_CHAR()
END_MESSAGE_MAP()void CRegexEdit::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags)
{// 处理删除键和退格键if (nChar == VK_BACK || nChar == VK_DELETE) {CEdit::OnChar(nChar, nRepCnt, nFlags);return;}CString strCurrent;GetWindowText(strCurrent);// 获取光标当前位置int nStartChar, nEndChar;GetSel(nStartChar, nEndChar); // 获取当前选区的起始和结束位置std::string strText(CT2A(strCurrent.GetBuffer()));strText.insert(strText.begin() + nStartChar, (char)nChar);bool bValid = m_customComparator ? m_customComparator(strText) : IsValueInRange(strText);if (std::regex_match(strText, GetCurrentRegex()) && bValid) {CEdit::OnChar(nChar, nRepCnt, nFlags);}else {if (m_invalidInputCallback) {m_invalidInputCallback();}}
}

如何使用自定义编辑框

定义编辑框控件

在对话框类中,添加一个自定义的编辑框控件:

CRegexEdit m_editNumeric;

初始化控件

在对话框的 OnInitDialog 方法中初始化控件:

m_editNumeric.SubclassDlgItem(IDC_EDIT_NUMERIC, this);
m_editNumeric.SetRegexType(RegexType::Decimal);  // 设置为输入数字
m_editNumeric.SetValueRange(0.0, 100.0);         // 设置数值范围为 0.0 到 100.0
m_editNumeric.SetInvalidInputCallback([]() {AfxMessageBox(_T("输入不合法,请检查输入内容!"));
});

查找和替换操作

void CYourDialog::OnFindAndReplace()
{std::string foundText;// 查找匹配的电话号码if (m_editBox.FindMatch(R"(\d{3}-\d{3}-\d{4})", foundText)) {AfxMessageBox(CString("Found: ") + CString(foundText.c_str()));}else {AfxMessageBox(_T("No match found."));}// 替换所有数字为 "[REDACTED]"m_editBox.ReplaceMatch(R"(\d+)", "[REDACTED]");
}

常见替换场景

隐藏敏感信息

  • 匹配模式:R"(\d{3}-\d{3}-\d{4})"(电话号码)
  • 替换为:"***-***-****"
m_editBox.ReplaceMatch(R"(\d{3}-\d{3}-\d{4})", "***-***-****");

标准化日期格式

  • 匹配模式:R"(\d{2})/(\d{2})/(\d{4})"(如 "12/31/2023")
  • 替换为:"$3-$1-$2"(如 "2023-12-31")
m_editBox.ReplaceMatch(R"((\d{2})/(\d{2})/(\d{4}))", "$3-$1-$2");

提取并标记关键词

  • 匹配模式:R"(error|warning|critical)"(关键词)
  • 替换为:"<b>$1</b>"(加粗显示)
m_editBox.ReplaceMatch(R"(error|warning|critical)", "<b>$1</b>");

常见正则表达式

以下是一些常用的正则表达式规则,可以直接在 SetCustomRegex 中使用:

功能正则表达式描述
整数^-?\d+$可正可负的整数
浮点数(小数)^-?\d+(\.\d+)?$可正可负的小数和整数
邮箱地址^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$邮箱格式校验
日期(yyyy-mm-dd)^\d{4}-\d{2}-\d{2}$日期格式校验
电话号码(11 位数字)^1[3-9]\d{9}$简单的中国手机号码格式校验

常用的编辑框消息宏

  • 用户输入相关
    • ON_WM_CHAR
    • ON_WM_KEYDOWN
    • ON_WM_KEYUP
  • 焦点相关
    • ON_WM_SETFOCUS
    • ON_WM_KILLFOCUS
  • 内容变化相关
    • ON_EN_CHANGE
    • ON_EN_UPDATE
  • 鼠标相关
    • ON_WM_LBUTTONDOWN
    • ON_WM_LBUTTONUP
    • ON_WM_MOUSEMOVE
    • ON_WM_RBUTTONDOWN
    • ON_WM_RBUTTONUP
    • ON_WM_MOUSEHOVER
  • 外观绘制相关
    • ON_WM_CTLCOLOR
    • ON_WM_NCPAINT
    • ON_WM_PAINT
消息宏对应的函数说明
ON_WM_CHAROnChar处理字符输入事件(如按键字符)。
ON_WM_KEYDOWNOnKeyDown处理键盘按下事件(如功能键、方向键)。
ON_WM_KEYUPOnKeyUp处理键盘释放事件。
ON_WM_SETFOCUSOnSetFocus编辑框获得输入焦点时触发。
ON_WM_KILLFOCUSOnKillFocus编辑框失去输入焦点时触发。
ON_EN_CHANGEOnChange编辑框内容发生变化时触发。
ON_EN_UPDATEOnUpdate编辑框内容即将更新时触发。
ON_WM_LBUTTONDOWNOnLButtonDown鼠标左键在编辑框内按下时触发。
ON_WM_LBUTTONUPOnLButtonUp鼠标左键在编辑框内释放时触发。
ON_WM_MOUSEMOVEOnMouseMove鼠标在编辑框内移动时触发。
ON_WM_RBUTTONDOWNOnRButtonDown鼠标右键在编辑框内按下时触发。
ON_WM_RBUTTONUPOnRButtonUp鼠标右键在编辑框内释放时触发。
ON_WM_CTLCOLOROnCtlColor更改编辑框的背景颜色或文本颜色。
ON_WM_MOUSEHOVEROnMouseHover鼠标悬停在编辑框时触发。
ON_WM_NCPAINTOnNcPaint非客户区(如边框)需要绘制时触发。
ON_WM_PAINTOnPaint编辑框需要重绘时触发(如更新内容时)。

总结

通过自定义 CRegexEdit,我们可以将正则表达式强大的字符串校验能力与 MFC 的编辑框控件结合起来,实现灵活的输入校验功能。借助枚举类型的封装和回调函数的支持,CRegexEdit 可以轻松适应多种场景,既满足通用需求,也能满足复杂的自定义需求。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词