You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

子类化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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.25 18:32:22