Ian*_*oyd 11 windows winapi custom-draw
我需要实现EM_SETCUEBANNER的功能,其中文本提示出现在Edit控件中:

问题是我无法使用通用控件的第6版,这是获得Microsoft提供的cue横幅实现所需的.
我已经研究过简单地改变编辑控件的文本和字体格式
Dark Gray Italic Text
Run Code Online (Sandbox Code Playgroud)
但它会抛出Change事件(由更高的组件库提供的组件包装器),我无法找到避免的方法.
所以我改为自定义绘制文本,在控件未聚焦且为空时绘制Cue Banner文本,否则依赖默认绘制.
Edit控件不能很好地公开自定义绘图机制,如ListView,TreeView和其他提供的.
其他人已经研究过它,但这似乎是一项几乎不可能完成的任务:
从事情的方式来看,我将不得不处理以下消息:
- WM_ERASEBKGND,WM_PAINT(原因很明显)
- WM_SETFOCUS,WM_KILLFOCUS(防止白条显示 - 如上所述)
- WM_CHAR(处理和更新控件中的文本)
而且我还需要找到一种在控件中显示插入符号的方法,因为我还没有找到一种方法允许Windows为我这样做而不用画我提到的白条.
这将会非常好玩.:翻白眼:
鉴于Windows Edit控件从未打算自定义绘制:有没有人知道如何自定义绘制Windows编辑控件?
注意:我也会接受解决问题的答案,而不是回答我的问题.但是其他任何想要自定义绘制编辑控件的人,遇到这个问题,都可能会有一个答案.
自定义绘制编辑控件基本上是不可能的.有一些特殊情况,你做得很少,可以侥幸逃脱,但你可能会在下一版Windows中(或者有人在旧版本上运行你的应用程序,或通过终端服务等)破坏风险.
仅仅接管WM_PAINT和WM_ERASEBKGROUND还不够好,因为控件有时也会在其他消息上绘制.
你最好只编写自己的编辑控件.我知道这是一项繁重的工作,但从长远来看,它不会试图破解你接管所有Edit控件的绘图代码.
我记得在过去的好日子里,每个人都使用按钮控件的子类来添加颜色和图形等等.事情是,有一天我坐下来写了我自己的按钮窗口类.并且它比我们在源树中的子类和自定义绘制Windows按钮的代码少.
创建一个自己的窗口类,它看起来像是空的编辑控件,它绘制提示文本并显示插入符并具有焦点.同时创建编辑控件,但将其放在窗口后面.(或隐藏它)
然后当你得到第一个WM_CHAR消息(或WM_KEYDOWN?).您将窗口置于编辑控件之后,将焦点放在编辑上,然后传递WM_CHAR消息.从那时起,编辑控件将接管.
如果您需要在编辑为空时返回显示提示文本,则可以从编辑控件收听EN_CHANGE通知.但我认为只有在编辑失去焦点并且为空时返回到提示文本才行.
对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>");
Run Code Online (Sandbox Code Playgroud)
回调:
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;
Run Code Online (Sandbox Code Playgroud)
编写自己的EDIT控件(我实际上已经多次执行,与内置编辑相比,部分完整程度)如果你做到最低限度(也许只有英文版,只有基本插入符号支持),那就不多了.如果您希望在具有可变大小的群集,范围选择,IME支持,具有复制和粘贴的上下文菜单,高对比度模式以及文本到语音等可访问性功能的复杂脚本上进行插入符号导航,则需要进行大量工作.因此,与许多其他答案不同,我建议不要仅针对提示横幅文本实现自己的EDIT控件.
| 归档时间: |
|
| 查看次数: |
13718 次 |
| 最近记录: |