WinAPI C++:Rich Edit控件自定义字符格式及dwCookie问题
关于Rich Edit控件自定义字符格式的问题解答
一、CHARFORMAT2W::dwCookie的正确用法
你之前遇到dwCookie读写失败的核心问题是没有在CHARFORMAT2W的dwMask中包含CFM_COOKIE位。这个字段并不是默认会被控件保存的,必须显式指定掩码:
- 设置时:调用
EM_SETCHARFORMAT前,把CHARFORMAT2W.dwMask设为CFM_COOKIE(如果同时要设置其他格式,用位或组合,比如CFM_COOKIE | CFM_COLOR),控件才会保存dwCookie的值。 - 获取时:同样要在
CHARFORMAT2W.dwMask中指定CFM_COOKIE,否则返回的dwCookie会是0。
实际可用的代码片段:
// 设置自定义标识到dwCookie CHARFORMAT2W cfSet = {0}; cfSet.cbSize = sizeof(CHARFORMAT2W); cfSet.dwMask = CFM_COOKIE; // 必须包含这个掩码 cfSet.dwCookie = 0x02; // 自定义位标识 SendMessage(hRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, reinterpret_cast<LPARAM>(&cfSet)); // 读取dwCookie的值 CHARFORMAT2W cfGet = {0}; cfGet.cbSize = sizeof(CHARFORMAT2W); cfGet.dwMask = CFM_COOKIE; // 同样要指定掩码 SendMessage(hRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, reinterpret_cast<LPARAM>(&cfGet)); // 此时cfGet.dwCookie应返回0x02,前提是选区格式已被设置
注意:dwCookie和字符格式绑定,若同一选区内有多种格式,EM_GETCHARFORMAT返回的dwCookie会是选区所有字符dwCookie的交集(不一致则返回0),处理混合格式时需留意。
二、实现两种自定义格式的更优方案
针对你需要的“隐藏置灰”和“绝对不缩放”,推荐以下更贴合需求的方案:
1. 隐藏置灰(用户可见的灰色)
直接用CHARFORMAT2W.crTextColor设置灰色(比如RGB(128, 128, 128))实现视觉效果,再用dwCookie的某一位(比如0x02)标记这是应用专属置灰格式,区别于用户手动设置的灰色:
CHARFORMAT2W cf = {0}; cf.cbSize = sizeof(CHARFORMAT2W); cf.dwMask = CFM_COLOR | CFM_COOKIE; cf.crTextColor = RGB(128, 128, 128); cf.dwCookie = 0x02; // 标记为隐藏置灰 SendMessage(hRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, reinterpret_cast<LPARAM>(&cf));
2. 绝对不缩放(文本不随程序元素缩放)
核心是使用固定像素大小的字体,不依赖系统DPI或程序缩放因子,结合dwCookie标记:
// 创建固定大小字体(比如12px,不随DPI变化) HFONT hFixedFont = CreateFontW( -12, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, FF_DONTCARE, L"Segoe UI" ); // 设置绝对不缩放格式 CHARFORMAT2W cf = {0}; cf.cbSize = sizeof(CHARFORMAT2W); cf.dwMask = CFM_FONT | CFM_COOKIE; cf.hFont = hFixedFont; cf.dwCookie = 0x04; // 标记为绝对不缩放 SendMessage(hRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, reinterpret_cast<LPARAM>(&cf));
后续判断时,检查dwCookie的位和对应hFont即可。
3. 进阶灵活方案:使用EM_SETCUSTFORMAT
Win10自带的RICHEDIT50W支持EM_SETCUSTFORMAT,可注册字符串标识的自定义格式,扩展性更强:
// 注册"HiddenGray"自定义格式 CUSTFORMAT custFormat = {0}; custFormat.cbSize = sizeof(CUSTFORMAT); wcscpy_s(custFormat.szName, L"HiddenGray"); custFormat.dwMask = CFM_COLOR; custFormat.cf.crTextColor = RGB(128, 128, 128); SendMessage(hRichEdit, EM_SETCUSTFORMAT, 0, reinterpret_cast<LPARAM>(&custFormat)); // 注册"NoScale"自定义格式 wcscpy_s(custFormat.szName, L"NoScale"); custFormat.dwMask = CFM_FONT; custFormat.cf.hFont = hFixedFont; SendMessage(hRichEdit, EM_SETCUSTFORMAT, 0, reinterpret_cast<LPARAM>(&custFormat)); // 应用自定义格式 CHARFORMAT2W cf = {0}; cf.cbSize = sizeof(CHARFORMAT2W); cf.dwMask = CFM_CUSTFORMAT; wcscpy_s(cf.szCustFormat, L"HiddenGray"); SendMessage(hRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, reinterpret_cast<LPARAM>(&cf));
这种方式无需管理位标识,直接通过字符串判断格式,后续扩展新格式更方便。
三、避坑提醒
- 不要使用CHARFORMAT2W的预留字段(如dwReserved)存储自定义数据,这些字段是控件内部使用的,版本更新可能导致兼容性问题。
- 处理大范围文本自定义格式时,避免维护外部格式数组(如与文本字符一一对应的标识数组),文本插入、删除会导致同步成本极高,优先使用控件自带的格式存储机制。
内容的提问来源于stack exchange,提问作者rpatters1
相关产品推荐
相关产品推荐

