所有者绘制按钮,WM_CTLCOLORBTN 和 WM_DRAWITEM(清除 HDC) [英] Owner-drawn button, WM_CTLCOLORBTN and WM_DRAWITEM (clearing an HDC)
问题描述
我正在尝试实现一个简单的自绘按钮,它只包含来自画笔的图像.
I'm trying to implement a simple owner-drawn button, which will just contain an image from a brush.
这是我的代码(WTL,但很简单):
Here's my code (WTL, but it's quite straightforward):
case WM_CTLCOLORBTN:
dc.SetBkMode(TRANSPARENT);
POINT pt = { 0 };
button.MapWindowPoints(m_hWnd, &pt, 1);
dc.SetBrushOrg(-pt.x, -pt.y, NULL);
return m_brushHeader;
到目前为止一切正常,但为了适当的键盘支持,我必须添加焦点矩形.所以现在我也在处理 WM_DRAWITEM
消息:
Everything works fine so far, but for proper keyboard support, I have to add focus rectangle.
So now I'm also handling the WM_DRAWITEM
message:
case WM_DRAWITEM:
if(lpDrawItemStruct->itemAction & (ODA_DRAWENTIRE | ODA_FOCUS))
{
if((lpDrawItemStruct->itemState & ODS_FOCUS) &&
!(lpDrawItemStruct->itemState & ODS_NOFOCUSRECT))
{
dc.DrawFocusRect(&lpDrawItemStruct->rcItem);
}
else
{
// Need to remove the rectangle here!
}
break;
}
break;
矩形已正确添加,但是当焦点移动到不同的按钮时,并且我收到 ODA_DRAWENTIRE
请求,我必须清除它.
The rectangle is properly added, but when the focus is moved to a different button, and I receive the ODA_DRAWENTIRE
request, I have to clear it.
如何清除 HDC 的内容?我发现只有用颜色等填充它的方法.我需要使它空/透明,就像在使用 DrawFocusRect
之前一样.
How do I clear the content of the HDC? I found only methods of filling it with color, etc. I need to make it empty/transparent, like it was before using DrawFocusRect
.
附言应用程序使用视觉样式,即 ComCtl32.dll 版本 6.
P.S. The application uses visual styles, i.e. ComCtl32.dll Version 6.
推荐答案
更新:在过去的 15 年里,我一直生活在时间胶囊中,最初发布的答案并未解决如何解决围绕 视觉样式(见下文).
Update: I've been living in a time capsule for the past 15 years and initially posted an answer that doesn't address how to solve the issues revolving around Visual Styles (see below).
启用视觉样式后,的行为发生了变化WM_DRAWITEM
消息:DRAWITEMSTRUCT
s itemAction
字段不再在焦点丢失时设置 ODA_FOCUS
位.结果是无法再应用移除此答案底部的焦点矩形的解决方案.
With Visual Styles enabled there is a change in behavior for the WM_DRAWITEM
message: The DRAWITEMSTRUCT
s itemAction
field no longer has the ODA_FOCUS
bit set on focus loss. The result is that the solution to remove the focus rectangle towards the bottom of this answer can no longer be applied.
要移除启用了视觉样式的焦点矩形,需要再次渲染控件.消息处理程序的以下代码片段显示了如何执行此操作:
To remove the focus rectangle with visual styles enabled requires rendering the control again. The following code snippet for the message handler shows how to do this:
switch ( message ) {
// ...
case WM_DRAWITEM: {
const DRAWITEMSTRUCT& dis = *(DRAWITEMSTRUCT*)lParam;
if ( dis.itemAction & ODA_DRAWENTIRE ) {
// Render the control
// ...
// If the control has the input focus...
if ( dis.itemState & ODS_FOCUS ) {
// Render the focus rectangle
DrawFocusRect( dis.hDC, &dis.rcItem );
}
}
}
// ...
}
<小时>
不需要在失去焦点时重新绘制整个控件.DrawFocusRect
以 XOR 模式呈现并且可以再次应用相同的操作将其移除.
Redrawing the entire control upon focus loss is not required. DrawFocusRect
is rendered in XOR mode and can be removed by applying the same operation a second time.
渲染焦点矩形的逻辑由两部分组成:
The logic to render the focus rectangle consists of two parts:
- 如果
itemAction
包含ODA_FOCUS
渲染焦点矩形而不考虑任何其他状态.这会切换可见性. - 否则,仅渲染焦点矩形,如果
itemState
包含ODS_FOCUS
.这是必要的,以便正确考虑初始状态.
- If
itemAction
containsODA_FOCUS
render the focus rectangle irrespective of any other state. This toggles the visibility. - Otherwise, only render the focus rectangle, if
itemState
containsODS_FOCUS
. This is necessary so that the initial state is properly accounted for.
以下代码演示了此策略.
The following code demonstrates this strategy.
资源.h:
#define IDD_MAINDLG 101
DlgBasedWin32.rc(声明一个只有确定"和取消"按钮的简单对话框):
DlgBasedWin32.rc (Declaring a simple dialog with just an OK and Cancel button):
#include "resource.h"
/////////////////////////////////////////////////////////////////////////////
//
// Dialog
//
IDD_MAINDLG DIALOGEX 0, 0, 309, 176
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "Dialog"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
CONTROL "OK",IDOK,"Button",BS_OWNERDRAW | WS_TABSTOP,198,155,50,14
CONTROL "Cancel",IDCANCEL,"Button",BS_OWNERDRAW | WS_TABSTOP,252,155,50,14
END
DlgBasedWin32.cpp(创建主对话框和消息循环):
DlgBasedWin32.cpp (Creating the main dialog and message loop):
#include <windows.h>
#include "resource.h"
// Forward declarations of functions included in this code module:
INT_PTR CALLBACK DlgProc( HWND, UINT, WPARAM, LPARAM );
int APIENTRY _tWinMain( HINSTANCE hInstance,
HINSTANCE /*hPrevInstance*/,
LPTSTR /*lpCmdLine*/,
int /*nCmdShow*/)
{
HWND hDlg = CreateDialogW( hInstance, MAKEINTRESOURCEW( IDD_MAINDLG ),
NULL, DlgProc );
ShowWindow( hDlg, SW_SHOW );
UpdateWindow( hDlg );
MSG msg = { 0 };
// Main message loop:
while ( GetMessageW( &msg, NULL, 0, 0 ) )
{
if ( !IsDialogMessageW( hDlg, &msg ) ) {
TranslateMessage( &msg );
DispatchMessageW( &msg );
}
}
return (int) msg.wParam;
}
DlgBasedWin32.cpp(对话框消息处理程序):
DlgBasedWin32.cpp (Dialog message handler):
// Message handler for IDD_MAINDLG
INT_PTR CALLBACK DlgProc( HWND hDlg,
UINT message,
WPARAM wParam,
LPARAM lParam )
{
switch ( message )
{
case WM_INITDIALOG:
return (INT_PTR)TRUE;
case WM_COMMAND:
if ( LOWORD( wParam ) == IDOK || LOWORD( wParam ) == IDCANCEL ) {
DestroyWindow( hDlg );
return (INT_PTR)TRUE;
}
break;
case WM_DESTROY:
PostQuitMessage( 0 );
return (INT_PTR)TRUE;
case WM_DRAWITEM: {
WORD wID = (WORD)wParam;
const DRAWITEMSTRUCT& dis = *(DRAWITEMSTRUCT*)lParam;
// Focus change?
if ( dis.itemAction & ODA_FOCUS ) {
// Toggle focus rectangle
DrawFocusRect( dis.hDC, &dis.rcItem );
}
else if ( dis.itemAction & ODA_DRAWENTIRE ) {
// Not a focus change -> render rectangle if requested
if ( dis.itemState & ODS_FOCUS ) {
DrawFocusRect( dis.hDC, &dis.rcItem );
}
}
return (INT_PTR)TRUE;
}
}
return (INT_PTR)FALSE;
}
上面的代码显示了一个简单的对话框,只有一个确定和取消按钮.按钮设置了 BS_OWNERDRAW
样式,WM_DRAWITEM
处理程序仅渲染焦点矩形;否则按钮保持不可见.完整的键盘和鼠标支持通过 IsDialogMessage
和默认消息处理程序.
The code above displays a simple dialog with just an OK and Cancel button. The buttons have the BS_OWNERDRAW
style set, and the WM_DRAWITEM
handler merely renders the focus rectangle; the buttons remain otherwise invisible. Full keyboard and mouse support is implemented through IsDialogMessage
and the default message handler, respectively.
这篇关于所有者绘制按钮,WM_CTLCOLORBTN 和 WM_DRAWITEM(清除 HDC)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!