代码之家  ›  专栏  ›  技术社区  ›  Ian Boyd

Win32:如何自定义绘制编辑控件?

  •  11
  • Ian Boyd  · 技术社区  · 15 年前

    我需要实现以下功能: em_setcuebranner ,其中文本提示出现在编辑控件内:

    关键是我不能使用通用控件的版本6,这是获得Microsoft提供的提示横幅实现所必需的。

    我研究过简单地将编辑控件的文本和字体格式更改为

    深灰色斜体文本
    < /代码> 
    
    

    但它会抛出由更高组件库提供的组件包装,即我找不到避免的方法。

    因此,我改为自定义绘制文本,在控件未聚焦且为空时绘制提示横幅文本,否则依赖默认绘制。

    编辑控件不能很好地公开自定义绘图机制,like listview,treeview and others provide.

    Other people have looked into it,but it似乎是一项几乎不可能完成的任务:

    < Buff行情>

    从目前的情况来看,我会 必须处理以下问题 消息:

    • wm_erasebkgnd,wm_paint(明显原因)
    • wm_setfocus,wm_killfocus(to prevent 显示的白色条-- 如上所述)
    • wm_char.(处理并更新 控件中的文本)

    我还需要找到一个方法 显示控件中的插入符号, 因为我找不到允许 Windows为我做这件事也没有 画我提到的白条。

    这会很有趣的。罗尔斯:

    < /块引用>

    考虑到Windows编辑控件从来就不是自定义绘制的:有人知道如何自定义绘制Windows编辑控件吗?


    注意:我还将接受解决我的问题的答案,而不是回答我的问题。但是,任何其他想要自定义绘制编辑控件的人,遇到这个问题时,都可能想要一个答案。,其中文本提示出现在编辑控件中:

    Example of cue banner in edit control

    关键是我不能使用通用控件的版本6,这是获得微软提供的提示横幅实现所必需的。

    我研究过简单地将编辑控件的文本和字体格式更改为

    Dark Gray Italic Text
    

    但它会扔变化事件(component wrapper provided by higher component library)我无法避免。

    所以我改为自定义绘制文本,在控件未聚焦且为空时绘制提示横幅文本,否则依赖默认绘制。

    编辑控件不能很好地公开自定义绘图机制,like ListView, TreeView and others provide.

    Other people have looked into it但这似乎是一项几乎不可能完成的任务:

    从目前的情况来看,我会 必须处理以下问题 信息:

    • wm_erasebkgnd,wm_油漆(明显原因)
    • wm_setfocus,wm_killfocus(为了防止 显示的白色条-- 如上所述)
    • 沃姆查尔(处理和更新 控件中的文本)

    我还需要找到一个方法 显示控件中的插入符号, 因为我找不到允许 Windows为我做这件事也没有 画我提到的那条白条。

    这会很有趣的。罗勒斯:

    考虑到Windows编辑控件从来就不是自定义绘制的:有人知道如何自定义绘制Windows编辑控件吗?


    注释:我也会接受解决我问题的答案,而不是回答我的问题。但是,任何其他想要定制一个编辑控件的人,遇到这个问题时,可能会想要一个答案。

    5 回复  |  直到 8 年前
        1
  •  9
  •   John Knoeller    15 年前

    自定义绘图编辑控件基本上是不可能的。有一些特殊的情况是,你做的太少,可以摆脱它,但你的风险很严重,在下一个版本的Windows(或当有人运行你的应用在旧版本,或通过终端服务等)。

    仅仅接管wm-paint和wm-erasebkground还不够好,因为控件有时也会在其他消息上绘制。

    你最好只写自己的编辑控件。我知道这是一个巨大的工作量,但从长远来看,它将比试图黑客的方式来接管所有编辑控件的绘图代码少工作。

    我记得以前的美好时光,那时每个人都用子类的按钮控件来添加颜色和图形等。事情是,有一天我坐下来,刚刚写了自己的按钮窗口类。它的代码比我们在源代码树中拥有的要少,可以对windows按钮进行子类化和自定义绘制。

        2
  •  5
  •   John Knoeller    15 年前

    创建一个自己的窗口类,它看起来像一个空的编辑控件,它绘制提示文本并显示一个插入符号并具有焦点。也创建编辑控件,但将其放在窗口后面。(或者把它藏起来)

    然后,当您收到第一个wm_char消息(或wm_keydown?)。您将窗口放在编辑控制项后面,将焦点放在编辑项上,然后打开wm_char消息。从那时起,编辑控件将接管。

    如果需要在编辑变为空时返回到显示提示文本,则可以从编辑控件中听取en_更改通知。但是我认为只有当编辑失去焦点并且是空的时候才返回提示文本是可以的。

        3
  •  5
  •   Dwayne Robinson    9 年前

    对编辑控件进行子类化对我来说很好——在编辑对象属性时需要向用户显示一些格式信息(有些属性可以是多行)。正如阿德里安在回答中所说,重要的是调用编辑控件的过程 之前 你自己画的。随后调用它或发布自己的beginpaint/endpaint(返回0或defwindowproc)给我带来了一些问题,从根本不显示的文本到只在调整大小时显示而不在编辑后显示的文本,到留下屏幕上残留的插入符号。这样,无论编辑控件的其他重新绘制时间如何,我都没有遇到任何问题。

    一些设置:

    SetWindowSubclass(attributeValuesEdit, &AttributeValueEditProcedure, 0, reinterpret_cast<DWORD_PTR>(this));
    
    // Not only do multiline edit controls fail to display the cue banner text,
    // but they also ignore the Edit_SetCueBannerText call, meaning we can't
    // just call GetCueBannerText in the subclassed function. So store it as
    // a window property instead.
    SetProp(attributeValuesEdit, L"CueBannerText", L"<attribute value>");
    

    回调:

    LRESULT CALLBACK AttributeValueEditProcedure(
        HWND hwnd,
        UINT message,
        WPARAM wParam,
        LPARAM lParam,
        UINT_PTR subclassId,
        DWORD_PTR data
        )
    {
    
    ...
    
    case WM_PRINTCLIENT:
    case WM_PAINT:
        {
            auto textLength = GetWindowTextLength(hwnd);
            if (textLength == 0 && GetFocus() != hwnd)
            {
                // Get the needed DC with DCX_INTERSECTUPDATE before the EDIT
                // control's WM_PAINT handler calls BeginPaint/EndPaint, which
                // validates the update rect and would otherwise lead to drawing
                // nothing later because the region is empty. Also, grab it from
                // the cache so we don't mess with the EDIT's DC.
                HDC hdc = (message == WM_PRINTCLIENT)
                    ? reinterpret_cast<HDC>(wParam)
                    : GetDCEx(hwnd, nullptr, DCX_INTERSECTUPDATE|DCX_CACHE|DCX_CLIPCHILDREN | DCX_CLIPSIBLINGS);
    
                // Call the EDIT control so that the caret is properly handled,
                // no caret litter left on the screen after tabbing away.
                auto result = DefSubclassProc(hwnd, message, wParam, lParam);
    
                // Get the font and margin so the cue banner text has a
                // consistent appearance and placement with existing text.
                HFONT font = GetWindowFont(hwnd);
                RECT editRect;
                Edit_GetRect(hwnd, OUT &editRect);
    
                // Ideally we would call Edit_GetCueBannerText, but since that message
                // returns nothing when ES_MULTILINE, use a window property instead.
                auto* cueBannerText = reinterpret_cast<wchar_t*>(GetProp(hwnd, L"CueBannerText"));
    
                HFONT previousFont = SelectFont(hdc, font);
                SetTextColor(hdc, GetSysColor(COLOR_GRAYTEXT));
                SetBkMode(hdc, TRANSPARENT);
                DrawText(hdc, cueBannerText, int(wcslen(cueBannerText)), &editRect, DT_TOP|DT_LEFT|DT_NOPREFIX|DT_NOCLIP);
                SelectFont(hdc, previousFont);
    
                ReleaseDC(hwnd, hdc);
    
                // Return the EDIT's result (could probably safely just return zero here,
                // but seems safer to relay whatever value came from the edit).
                return result;
            }
        }
        break;
    

    编写自己的编辑控件(与内置控件相比,我实际上已经做了不止一次,在一定程度上是完整的)并不是什么工作,如果你只做最少的裸代码(可能只有基本的插入符号支持的英语),但如果你想在复杂的脚本上使用插入符号导航,并使用可变大小的簇,sel范围选择、输入法支持、带复制和粘贴的上下文菜单、高对比度模式和辅助功能(如文本到语音转换)。因此,与其他许多答案不同,我建议 只为提示横幅文本实现自己的编辑控件。

        4
  •  3
  •   Adrian McCarthy    15 年前

    子类化编辑控件。把手 WM_PAINT 首先调用最初的窗口过程,然后,如果它是空的并且不在焦点上,则绘制提示文本。将其他所有消息传递给原始窗口过程。

    我已经做到了——它起作用了。编码专家的问题似乎不适用于您的情况。我相信他想在外表上做得更多。为了提高性能,似乎编辑控件在 水彩画 处理(可能是为了性能)。这将使得几乎不可能完全控制外观。但是你可以画提示。

        5
  •  0
  •   Dmitriy Yurchenko    12 年前

    我还需要找到一种在控件中显示插入符号的方法, 因为我还没有找到一种方法可以让Windows在没有 还画了我提到的白条。

    如果您希望自己处理wm_paint,而不将消息转发到超类的原始windowproc,则不应忘记调用defwindowproc。以便绘制插入符号。 为了避免出现白条,应该使用setClassLongptr删除类画笔。 不知怎么的,保持你的DC的剪辑区域剪辑编辑controt的exttextout输出。 白色条可能是编辑控件传递给exttextout的不透明选项的结果。

    结论:写下你自己的控制。没有痛苦,就没有收获。

    推荐文章