我正在尝试实现一个简单的所有者绘制按钮,它只包含画笔中的图像.
这是我的代码(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;
Run Code Online (Sandbox Code Playgroud)
到目前为止一切正常,但为了正确的键盘支持,我必须添加焦点矩形.所以现在我也在处理这个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;
Run Code Online (Sandbox Code Playgroud)
矩形已正确添加,但当焦点移动到另一个按钮,并且我收到ODA_DRAWENTIRE请求时,我必须清除它.
如何清除HDC的内容?我发现只有用颜色等填充它的方法.我需要将它变成空的/透明的,就像使用之前一样DrawFocusRect.
PS该应用程序使用视觉样式,即ComCtl32.dll版本6.
更新: 过去15年来,我一直生活在一个时间囊中,最初发布的答案并没有解决如何解决围绕视觉样式的问题(见下文).
启用视觉样式后,WM_DRAWITEM消息的行为会发生变化:DRAWITEMSTRUCTs 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 );
}
}
}
// ...
}
Run Code Online (Sandbox Code Playgroud)
不需要在焦点丢失时重绘整个控件.DrawFocusRect在XOR模式下渲染,可以通过第二次应用相同的操作来删除.
渲染焦点矩形的逻辑由两部分组成:
itemAction包含ODA_FOCUS渲染焦点矩形而不管其他任何状态.这会切换可见性.itemState包含)ODS_FOCUS.这是必要的,以便适当地考虑初始状态.以下代码演示了此策略.
RESOURCE.H:
#define IDD_MAINDLG 101
Run Code Online (Sandbox Code Playgroud)
DlgBasedWin32.rc(只用一个OK和Cancel按钮声明一个简单的对话框):
#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
Run Code Online (Sandbox Code Playgroud)
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;
}
Run Code Online (Sandbox Code Playgroud)
DlgBasedWin32.cpp(Dialog消息处理程序):
// 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;
}
Run Code Online (Sandbox Code Playgroud)
上面的代码显示了一个简单的对话框,只有一个OK和Cancel按钮.按钮具有BS_OWNERDRAW样式集,WM_DRAWITEM处理程序仅呈现焦点矩形; 按钮保持不可见.全键盘和鼠标支持IsDialogMessage分别通过和默认消息处理程序实现.
| 归档时间: |
|
| 查看次数: |
3479 次 |
| 最近记录: |