子类化RichEdit控件后文本无法显示的问题求助
自定义RichEdit控件选中文本高亮颜色的问题与解决
问题描述
尝试子类化MSFTEDIT_CLASS类型的RichEdit控件以实现选中文本的自定义高亮颜色,使用以下代码子类化:
// subclass the rich edit control SetWindowSubclass(hwndEdit, RichEditSubclassProc, 0, 0);
运行后仅显示红色高亮,文本却无法显示。子类化处理函数代码如下:
LRESULT CALLBACK RichEditSubclassProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData) { static HBRUSH hCustomBrush = NULL; static COLORREF customColor = RGB(255, 0, 0); // set custom color to red for example switch (message) { case WM_PAINT: { // get the selection range CHARRANGE selRange; SendMessage(hWnd, EM_EXGETSEL, 0, (LPARAM)&selRange); // get the DC for the rich edit control PAINTSTRUCT ps; HDC hDC = BeginPaint(hWnd, &ps); if (selRange.cpMin != selRange.cpMax) { // create custom brush if it doesn't exist yet if (!hCustomBrush) { hCustomBrush = CreateSolidBrush(customColor); } // set the background mode to transparent SetBkMode(hDC, TRANSPARENT); // set the custom brush as the background brush HBRUSH hPrevBrush = (HBRUSH)SelectObject(hDC, hCustomBrush); // get the bounding rectangles for the selection RECT selStartRect, selEndRect; SendMessage(hWnd, EM_GETRECT, 0, (LPARAM)&selStartRect); SendMessage(hWnd, EM_GETRECT, 0, (LPARAM)&selEndRect); // adjust the selection rectangles to the client area RECT clientRect; GetClientRect(hWnd, &clientRect); OffsetRect(&selStartRect, -ps.rcPaint.left, -ps.rcPaint.top); OffsetRect(&selEndRect, -ps.rcPaint.left, -ps.rcPaint.top); IntersectRect(&selStartRect, &selStartRect, &clientRect); IntersectRect(&selEndRect, &selEndRect, &clientRect); // fill the background of the selected text with the custom brush FillRect(hDC, &selStartRect, hCustomBrush); FillRect(hDC, &selEndRect, hCustomBrush); // restore the previous background brush SelectObject(hDC, hPrevBrush); } // call the default painting function DefSubclassProc(hWnd, message, wParam, lParam); // release the DC for the rich edit control EndPaint(hWnd, &ps); break; } default: { return DefSubclassProc(hWnd, message, wParam, lParam); } } return 0; }
需求是制作支持用户自定义高亮颜色的RichEdit控件,但新版本RichEdit控件始终默认使用系统高亮色,无法修改。
解决思路
代码存在三个核心问题,对应修正方案如下:
- 绘制顺序颠倒:先填充高亮矩形再执行默认绘制,会导致默认绘制的文本被覆盖。正确顺序是先让RichEdit完成默认文本绘制,再叠加自定义高亮。
- 选中区域获取错误:
EM_GETRECT获取的是整个控件的客户区,并非选中文本的实际矩形。需通过EM_EXGETSEL配合EM_FORMATRANGE或EM_GETCHARPOS获取选中文本的精确区域。 - DC管理冲突:默认绘制过程应复用已BeginPaint的DC,避免重复获取DC导致绘制逻辑混乱。
修正后的WM_PAINT核心代码示例
case WM_PAINT: { PAINTSTRUCT ps; HDC hDC = BeginPaint(hWnd, &ps); // 先执行默认绘制,确保文本正常显示 LRESULT result = DefSubclassProc(hWnd, message, wParam, lParam); CHARRANGE selRange; SendMessage(hWnd, EM_EXGETSEL, 0, (LPARAM)&selRange); if (selRange.cpMin != selRange.cpMax) { if (!hCustomBrush) { hCustomBrush = CreateSolidBrush(customColor); } // 获取选中区域的精确矩形(简化示例,复杂多行选中需遍历行) FORMATRANGE fr = {0}; fr.hdc = fr.hdcTarget = hDC; fr.chrg = selRange; // 获取选中区域的边界矩形 RECT selRect; SendMessage(hWnd, EM_FORMATRANGE, TRUE, (LPARAM)&fr); selRect = fr.rc; // 设置绘制模式避免覆盖文本,使用XOR或透明背景 SetROP2(hDC, R2_XORPEN); FillRect(hDC, &selRect, hCustomBrush); SetROP2(hDC, R2_COPYPEN); } EndPaint(hWnd, &ps); return result; }
另外,新版本RichEdit(RichEdit 4.1+)可通过EN_SELCHANGE通知触发,结合CHARFORMAT2的CFM_BACKCOLOR掩码直接设置选中文本的背景色,无需子类化WM_PAINT,这种方式更稳定。
内容的提问来源于stack exchange,提问作者Chuck
相关产品推荐
相关产品推荐

