我正在尝试实现一个简单的所有者绘制按钮,它只包含画笔中的图像。
这是我的代码(WTL,但它非常简单):
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
消息:
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
请求时,我必须清除它。
如何清除HDC的内容?我发现只有用颜色等填充它的方法。我需要将它变为空/透明,就像使用DrawFocusRect
之前一样。
P.S。该应用程序使用视觉样式,即ComCtl32.dll版本6.
答案 0 :(得分:5)
<强>更新强> 在过去的15年里,我一直生活在一个时间胶囊中,并且最初发布的答案并没有解决如何解决围绕Visual Styles的问题(见下文)。
启用视觉样式后,WM_DRAWITEM
message的行为发生了变化:DRAWITEMSTRUCT
s itemAction
字段在焦点丢失时不再设置ODA_FOCUS
位。结果是,无法再应用将此焦点矩形移到此答案底部的解决方案。
要在启用视觉样式的情况下移除焦点矩形,需要再次渲染控件。消息处理程序的以下代码段显示了如何执行此操作:
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
以异或模式呈现,可以通过第二次应用相同的操作来删除。
渲染焦点矩形的逻辑由两部分组成:
itemAction
包含ODA_FOCUS
呈现焦点矩形,则无论其他状态如何。这会切换可见性。itemState
包含ODS_FOCUS
,则仅渲染焦点矩形。这是必要的,以便正确地考虑初始状态。以下代码演示了此策略。
resource.h中:
#define IDD_MAINDLG 101
DlgBasedWin32.rc(仅使用“确定”和“取消”按钮声明一个简单的对话框):
#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(创建主对话框和消息循环):
#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(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;
}
上面的代码显示一个简单的对话框,只有一个OK和Cancel按钮。按钮具有BS_OWNERDRAW
样式集,WM_DRAWITEM
处理程序仅呈现焦点矩形;按钮保持不可见。全键盘和鼠标支持分别通过IsDialogMessage
和默认消息处理程序实现。