TM_PLAINTEXT模式下Rich Edit控件EM_SETPARAFORMAT失效问题咨询
背景
我正在开发一个可程序化创建Rich Edit控件的API,允许调用者通过以下代码切换纯文本/富文本模式:
void SetUseRichText(bool state) { DWORD textMode = SendDlgItemMessageW(GetWindowHandle(), GetControlID(), EM_GETTEXTMODE, 0, 0); if (state) { textMode &= ~TM_PLAINTEXT; textMode |= TM_RICHTEXT; } else { textMode &= ~TM_RICHTEXT; textMode |= TM_PLAINTEXT; } SendDlgItemMessageW(GetWindowHandle(), GetControlID(), EM_SETTEXTMODE, textMode, 0); }
设置纯文本模式的核心目的是防止粘贴内容带格式,但同时希望API自身能修改除字体外的其他格式(目前已实现字体修改)。现在遇到的问题是:当控件处于TM_PLAINTEXT模式时,用于设置行距的EM_SETPARAFORMAT消息失效(返回0),因此有以下三个疑问:
- 是否有办法控制
TM_PLAINTEXT模式下Rich Edit控件的行距?比如不使用控件特定消息的方法? TM_PLAINTEXT和TM_RICHTEXT是位值,同时设置两者或都不设置分别代表什么含义?- 是否应该采用其他方法来防止粘贴带格式的文本?
问题解答
1. 纯文本模式下控制行距的方法
TM_PLAINTEXT模式会直接禁用Rich Edit的大部分格式处理逻辑,EM_SETPARAFORMAT这类格式消息会被忽略,常规方法无法生效,但可以通过两种替代方案实现:
自定义绘制(Owner Draw):
处理WM_DRAWITEM消息,自行计算每行文本的位置、行距,手动完成文本绘制。这种方式完全脱离控件自带的格式逻辑,需要自己处理换行、裁剪等细节,但能完全掌控行距表现。临时切换模式修改后切回:
在需要设置行距时,临时将控件切换到TM_RICHTEXT模式,发送EM_SETPARAFORMAT完成设置,再切回原模式。注意保存切换前的状态,避免覆盖用户设置:void SetLineSpacingInPlainText(int value) { HWND hWnd = GetDlgItem(GetWindowHandle(), GetControlID()); DWORD oldMode = SendMessageW(hWnd, EM_GETTEXTMODE, 0, 0); // 临时切换到富文本模式 SendMessageW(hWnd, EM_SETTEXTMODE, oldMode & ~TM_PLAINTEXT | TM_RICHTEXT, 0); PARAFORMAT2 pf{}; pf.cbSize = sizeof(pf); pf.dwMask = PFM_LINESPACING; pf.bLineSpacingRule = value ? 3 : 0; pf.dyLineSpacing = std::lround(value * 20.0); SendMessageW(hWnd, EM_SETPARAFORMAT, 0, reinterpret_cast<LPARAM>(&pf)); // 切回原模式 SendMessageW(hWnd, EM_SETTEXTMODE, oldMode, 0); }测试验证,这种方式设置的行距会被保留,切回纯文本模式后控件仍按设置的行距显示,同时依然阻止带格式的粘贴。
2. TM_PLAINTEXT和TM_RICHTEXT的位值含义
根据Rich Edit官方定义:
- 同时设置两者:属于未定义行为,控件表现可能因版本不同而变化,通常会优先遵循
TM_RICHTEXT逻辑,或忽略其中一个位,不建议这么设置。 - 都不设置:代表控件处于默认兼容模式,行为更接近早期Rich Edit版本(如1.0),部分格式功能受限,且不会严格阻止带格式粘贴,不推荐使用。
正常使用时应仅设置其中一个位:TM_PLAINTEXT禁用所有格式,TM_RICHTEXT启用完整格式支持。
3. 防止粘贴带格式文本的替代方案
如果需要保留部分格式控制能力(如行距),同时阻止粘贴格式,TM_PLAINTEXT模式过于激进,可采用更灵活的方案:
拦截粘贴消息:
处理WM_PASTE消息,手动获取剪贴板纯文本内容,用WM_SETTEXT或EM_REPLACESEL插入控件,绕过富文本粘贴逻辑:case WM_PASTE: { if (OpenClipboard(hWnd)) { HANDLE hData = GetClipboardData(CF_UNICODETEXT); if (hData) { wchar_t* pText = static_cast<wchar_t*>(GlobalLock(hData)); if (pText) { SendMessageW(hWnd, EM_REPLACESEL, TRUE, reinterpret_cast<LPARAM>(pText)); GlobalUnlock(hData); } } CloseClipboard(); return 0; // 阻止默认粘贴行为 } break; }使用
EM_SETOPTIONS禁用格式粘贴:
针对Rich Edit 4.1及以上版本,可设置ECO_NOPASTEFORMAT选项,直接禁止粘贴格式:SendMessageW(hWnd, EM_SETOPTIONS, ECOOP_SET, ECO_NOPASTEFORMAT);这种方式无需切换纯文本模式,控件仍保留格式设置能力(如行距、字体),同时仅粘贴纯文本内容。
内容的提问来源于stack exchange,提问作者rpatters1

