所有者绘制按钮,WM_CTLCOLORBTN 和 WM_DRAWITEM(清除 HDC) [英] Owner-drawn button, WM_CTLCOLORBTN and WM_DRAWITEM (clearing an HDC)

查看:30
本文介绍了所有者绘制按钮,WM_CTLCOLORBTN 和 WM_DRAWITEM(清除 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 消息:DRAWITEMSTRUCTs itemAction 字段不再在焦点丢失时设置 ODA_FOCUS 位.结果是无法再应用移除此答案底部的焦点矩形的解决方案.

With Visual Styles enabled there is a change in behavior for the WM_DRAWITEM message: The DRAWITEMSTRUCTs 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:

  1. 如果 itemAction 包含 ODA_FOCUS 渲染焦点矩形而不考虑任何其他状态.这会切换可见性.
  2. 否则,仅渲染焦点矩形,如果itemState 包含ODS_FOCUS.这是必要的,以便正确考虑初始状态.
  1. If itemAction contains ODA_FOCUS render the focus rectangle irrespective of any other state. This toggles the visibility.
  2. Otherwise, only render the focus rectangle, if itemState contains ODS_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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆