2009-12-23 129 views
10

我需要实现的EM_SETCUEBANNER,其中文本提示会出现一个编辑控件中的功能:Win32:如何自定义绘制Edit控件?

Example of cue banner in edit control

美中不足的是,我不能使用公共控件版本6,这是需要什么获取Microsoft提供的提示横幅的实现。

我已经研究过简单地改变编辑控件的文本,字体格式

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_PAINT(出于显而易见的原因)
  • WM_SETFOCUS,WM_KILLFOCUS(为了防止 白条从显示 - 如上所述 )
  • WM_CHAR(以处理和更新在控制的 文本)

我还需要找到一种方法, 显示插入记号在控制, ,因为我还没有找到一种方法来允许 Windows为我做这件事,也没有 画我提到的白色栏。

这会很有趣。 :rolleyes:

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


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

+0

你可以像我在Aero.Controls中的即时搜索框那样伪造它。http://bitbucket.org/factormystic/aero.controls – 2009-12-24 06:01:24

+0

我曾尝试过你的方法。我很好奇你是如何解决绘制“SearchBox”主题的问题的。但我看到你没有解决问题。 – 2009-12-24 15:10:10

+0

6年过去了,我只是说默认使用一些文本的CreateWindow。当用户单击控件时,将删除文本。您也可以将字体设置为斜体,并在点击时更改它。 – kundrata 2016-11-25 11:59:27

回答

10

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

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

你最好只写自己的编辑控件。我知道这是一项大量的工作,但从长远来看,它的工作量要少于试图接管所有编辑控件的绘图代码。

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

+0

你似乎是对的。我继承了Edit,处理WM_PAINT,如果我想绘制提示文本。有时(使用鼠标选择,在空的编辑中推回退),控件将自己绘制为空而不调用WM_PAINT。 另一方面,在编辑中存在** LOT **功能,并且我无法证明所有代码投资都是合理的。 (剪切/复制/粘贴,撤消缓冲区,IME,RTL等) – 2009-12-24 16:39:41

+1

是的,任何一种方式都不是一堆代码。我只是说不要欺骗自己,认为使用更少的代码就可以实现子类化。从长远来看,它可能不会。 – 2009-12-24 20:28:25

+1

我结束了编辑的子类化,触发一个响应其他消息的绘制,而不仅仅是'WM_PAINT'。其他一些导致内部绘图发生的消息,没有任何'WM_PAINT'发生,并要求我重新绘制其重绘:WM_SETFOCUS,WM_KILLFOCUS,WM_KEYUP,WM_KEYDOWN,并且当鼠标进入树叶。 – 2010-02-10 19:00:11

3

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

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

+0

问题是,并非所有的绘画都是在'WM_PAINT'中发生的。有时'EDIT'控件会在'WM_KILLFOCUS'之后自行绘制 - 并且*没有WM_PAINT消息将被发送到控件*(即它不会使自身失效)。这意味着你也必须处理WM_KILLFOCUS并在它之后触发你自己的绘图。和“WM_SETFOCUS”,“WM_KEYUP”和“WM_KEYDOWN”,以及其他导致不带“WM_PAINT”的绘图的事件。 – 2012-10-09 13:38:44

+0

@IanBoyd:我承认在其他时候编辑控制作弊和涂料,但我已经使用这种技术实现了提示文本,并且它工作得很好。 – 2012-10-09 16:14:57

+0

@AdrianMcCarthy'BeginPaint',在调用原始窗口过程之后有一个空的无效矩形区域(因为它已经处理)。在调用原始Windows过程之前调用'BeginPaint'将不起作用。创建我自己的设备上下文会导致闪烁(特别是右键单击事件),并且优化字符串绘图似乎相当困难(因为它需要限制它在任意区域更新)。总之,我想不出一个简单的方法来绘制提示横幅,而不是闪烁;非常有趣虽然:) – Pooven 2013-09-25 10:34:22

5

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

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

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

+0

有趣的方法,而不仅仅是子类。它需要两个独立的窗口,并在叠加控制和父窗口上都处理代码,但它会起作用。我想知道你用什么函数来复制EDIT控件的必要框架和样式,包括任何活动主题(ThemeDrawText?)。创建第二个编辑控件来匹配外观可能会更容易,然后使用另一个实际用于保存内容的编辑控件,同时在父级中注意EN_CHANGE。 – 2015-07-17 23:46:59

0

而且我也需要找到一种方法在控件中显示插入符号, 因为我还没有找到一种方法来允许Windows做到这一点对我来说没有 也画我所提到的白条。

如果您希望自己处理WM_PAINT而不将消息转发到超类的原始windowproc,则不应该忘记调用DefWindowProc。以便插入插入符号。 要避免白色条,应该使用SetClassLongPtr删除类刷。 并以某种方式将DC的剪辑区域保留为剪辑编辑controt的ExtTextOut输出。 白色条可能是由Edit控件传递给ExtTextOut的OPAQUE选项的结果。

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

3

对EDIT控件进行子类化对我来说效果很好 - 编辑对象属性时需要向用户显示一些格式信息(有些属性可能是多行)。重要的是,像Adrian在他的回答中所说的那样,在你自己的图纸上,在之前调用编辑控件的程序。之后调用它或发布自己的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; 

编写自己的编辑控制(我实际上已经超过一次,相比于内置偏度完整性如果你做最低限度的工作(也许只有英文基本的插入符支持),但是如果你想在可变大小的集群上进行复杂脚本的光标导航,范围选择,输入法支持,上下文菜单复制和粘贴,高对比度模式以及文字到语音等辅助功能。所以不像其他许多其他答案,我推荐而不是实现您自己的EDIT控件仅用于提示横幅文本。