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

我需要实现EM_SETCUEBANNER的function,其中文本提示出现在编辑控件中:

编辑控件中提示横幅的示例

问题是,我不能使用公共控件的版本6,这是获得微软提供的提示横幅所需的。

我已经看着简单地更改编辑控件的文本,并将字体格式为

Dark Gray Italic Text 

但它会抛出更改事件( 由更高的组件库提供的组件包装 ),我无法find一种方法来避免。

所以我反而要自定义绘制文本,当控件没有重点和空的时候绘制提示横条文本,否则依靠默认的绘画。

编辑控件不能很好地公开自定义绘图机制, 如ListView,TreeView和其他提供 。

其他人已经看过 ,但这似乎是一个几乎不可能完成的任务:

从现在看,我将不得不处理以下消息:

  • WM_ERASEBKGND,WM_PAINT (出于显而易见的原因)
  • WM_SETFOCUS,WM_KILLFOCUS (防止白色条显示 – 如上所述)
  • WM_CHAR (处理和更新控件中的文本)

而且我还需要find一种方法来在控件中显示插入符号,因为我还没有find一种方法来让Windows为我做这件事,而不用画上我提到的白色条。

这将会非常好玩。 :翻白眼:

鉴于Windows编辑控件从来没有被定制绘制:有谁知道如何自定义绘制Windows编辑控件?


注意 :我也会接受解决我的问题的答案,而不是回答我的问题。 但是任何想要自定义绘制Edit控件的人都会遇到这个问题,可能会想要一个答案。

Solutions Collecting From Web of "Win32:如何自定义绘制编辑控件?"

自定义绘图编辑控件基本上是不可能的。 有几个专门的例子,你做得很少,可以逃避,但是在下一次修订版本的Windows中(或者当有人在旧版本上运行你的应用程序,或者通过终端服务等)时,你可能会冒险破坏。

只是接管WM_PAINT和WM_ERASEBKGROUND是不够好的,因为控件有时也会在其他消息上绘制。

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

我记得在过去的时代,每个人都使用按钮控件的子类来添加颜色和图形等等。事情是,有一天我坐下来,写了我自己的按钮窗口类。 它比我们的源代码树中的子类和自定义绘制Windows按钮的代码少。

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

那么当你得到第一个WM_CHAR消息(或WM_KEYDOWN?)。 你把你的窗口放在编辑控件的后面,把焦点放在编辑上,并传递WM_CHAR消息。 从此编辑控件将接管。

您可以在编辑控件中收听EN_CHANGE通知,如果在编辑变为空白时需要返回显示提示文字。 但是我认为只有当编辑失去焦点并且是空的时候才能回到提示文本。

子类编辑控件。 首先调用原始窗口过程来处理WM_PAINT ,如果它是空的并且没有焦点,则绘制提示文本。 将其他所有消息传递给原始窗口过程。

我已经完成了这个工作。 CodeGuru人的问题似乎不适用于你的情况。 我相信他正在试图做更多的外观。 为了提高性能,看起来编辑控件在WM_PAINT处理之外进行一些更新(可能是为了提高性能)。 这将使几乎不可能完全控制外观。 但是你可以画出提示提示。

为我编写EDIT控件的子类很好 – 编辑对象属性时需要向用户显示一些格式信息(有些属性可能是多行)。 像Adrian在他的回答中所说的,重要的是你自己的绘图之前调用EDIT控件的过程。 之后调用它或者发出你自己的BeginPaint / EndPaint(带有返回0或者DefWindowProc),从文本中根本不显示任何内容,只在调整大小时显示,而不是在编辑之后显示,留下剩余的插入符号的屏幕垃圾。 有了这个,我没有任何问题,不管EDIT控件的其他重绘时间。

一些设置:

 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; 

编写自己的编辑控件(实际上我已经做了不止一次,比内置的程度更完整),如果你做的最低限度(也许只有基本的插入符号支持的英文),那么没有太多的工作,但是如果您要在具有可变大小群集的复杂脚本,范围选择,IME支持,具有复制和粘贴的上下文菜单,高对比度模式以及文本到语音的可访问性功能上进行光标导航,那么获得正确的工作是相当多的工作。 所以不像其他许多其他的答案,我建议实施你自己的编辑控件只是提示横幅文本。

而且我还需要找到一种方法来在控件中显示插入符号,因为我还没有找到一种方法来让Windows为我做这件事,而不用画上我提到的白色条。

如果你想自己处理WM_PAINT而不把消息转发到超类的原始windowproc,你不应该忘记调用DefWindowProc。 这样就可以得到插入的字符。 为了避免白色条,你应该使用SetClassLongPtr删除类刷。 并以某种方式保持你的DC的剪辑区域剪辑编辑controt的ExtTextOut输出。 白色条可能是通过Edit控件传递给ExtTextOut的OPAQUE选项的结果。

结论:写你自己的控制。 一分耕耘一分收获。