MFC VC ++自定义复选框图像

时间:2011-07-16 16:52:23

标签: c++ visual-studio-2010 checkbox mfc ownerdrawn

如何获得一个3状态复选框,以便为Indeterminate状态使用不同的位图?

我想更改我的3状态复选框使用的图像以使用不同的图像;控件是Win98风格的,这些复选框的不确定状态很难与禁用的复选框区分开来(这可能是为什么他们为WinXP风格的控件改变了这个,但由于我项目中的其他细节,我不能使用它们)

我正在使用Visual C ++ 2010,我在VS的资源编辑器中定义了一个8x8位图。位图的ID为IDB_INDET_CHECK

我不完全确定这样的标准“技术”是什么;我只是刚刚开始操作Windows控件和MFC。

我的第一次尝试是创建一个源自CTriButton的类CButton,覆盖DrawItem函数,并尝试自己绘制它。然后我使用SubclassDlgItem将我窗口中的一个复选框变成了这个类(我想?)。这...有点工作?复选框不再出现,如果我单击它应该在的位置,则会出现一个空的复选框,但不会发生任何其他情况(并且我的代码中的调试消息未被发送)。

这是相关的代码,但我不确定任何这是正确的。首先,来自我的窗口OnInitDialog的代码。

BOOL CAffixFilterDlg::OnInitDialog() // CAffixFilterDlg is my CDialog-derived window
{
    CDialog::OnInitDialog(); // call basic version

    // subclass a CButton-derived control with CTriButton
    if ( CBipedHead.SubclassDlgItem(IDC_HEAD, this) ) // CBipedHead is a CTriButton member of CAffixFilterDlg, IDC_HEAD is a checkbox
        SetWindowLong(CBipedHead.m_hWnd, GWL_STYLE, CBipedHead.GetStyle() | BS_OWNERDRAW); // set the ownerdraw style
    else // subclassing didn't work
        _ERROR("Subclassing failed."); // I do not see this error message, so SubclassDlgItem worked?

    // initialization continues, but is not relevant...
    UpdateWindow();
    Invalidate();

    return TRUE;
}

接下来,我的自定义按钮的代码为DrawItem

void CTriButton::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
{
    _DMESSAGE("Drawing TriButton"); // never see this message

    CDC dc;
    dc.Attach(lpDrawItemStruct->hDC);     //Get device context object
    int nWidth = GetSystemMetrics(SM_CXMENUCHECK);
    int nMargin = ( nWidth - 8 ) / 2;

    CRect textRt = lpDrawItemStruct->rcItem;
    textRt.right = textRt.right - nWidth - nMargin;

    CString text;
    GetWindowText(text);

    UINT textDrawState = DST_TEXT;
    if ( lpDrawItemStruct->itemState & ODS_DISABLED )
        textDrawState |= DSS_DISABLED;

    dc.DrawState(CPoint(textRt.left, textRt.top), textRt.Size(), text, textDrawState, TRUE, 0, (CBrush*)NULL);

    CRect rt = lpDrawItemStruct->rcItem;    // initial rect is for entire button
    rt.left = rt.right - nWidth;            // set left margin
    LONG center = ( rt.bottom + rt.top ) / 2;
    rt.top = center - nWidth/2;
    rt.bottom = center + nWidth/2;

    UINT checkDrawState = DFCS_BUTTONCHECK;
    if ( lpDrawItemStruct->itemState & ODS_DISABLED )
        checkDrawState |= DFCS_INACTIVE;

    if ( lpDrawItemStruct->itemState & ODS_CHECKED )
        checkDrawState |= DFCS_CHECKED;

    else if ( GetCheck() == BST_INDETERMINATE ) {
        _VMESSAGE("Indeterminate; custom draw.");

        CBitmap indet_check = CBitmap();
        indet_check.LoadBitmap(IDB_INDET_CHECK);

        CPoint pt = CPoint(rt.left + nMargin, rt.top + nMargin);
        CSize sz = CSize(8, 8);

        dc.DrawState(pt, sz, &indet_check, DST_BITMAP|DSS_NORMAL);
    }

    dc.DrawFrameControl(rt, DFC_BUTTON, checkDrawState);
}

2 个答案:

答案 0 :(得分:3)

在OnInitDialog()中,您需要在更改窗口样式后调用InvalidateRect(),否则它不知道需要重绘。更改窗口样式后调用UpdateWindow()也是个好主意。某些信息通常由公共控件缓存,并且在调用UpdateWindow()之前不会确认更改。

在DrawItem()中,您负责渲染控件的所有状态。你不应该调用CButton :: DrawItem(),因为它什么都不做。尝试以下内容:

void CTriButton::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
{
    CBitmap indet_check

    _DMESSAGE("Drawing TriButton"); // I never see this message
    int checkState = GetCheck();

    if ( checkState == BST_CHECKED )
    {
        indet_check.LoadBitmap(IDB_INDET_CHECK);
    }
    else if ( checkState == BST_UNCHECKED )
    {
        indet_check.LoadBitmap(IDB_INDET_UNCHECKED);
    }
    else if ( checkState == BST_INDETERMINATE )
    {
        indet_check.LoadBitmap(IDB_INDET_INDETERMINATE);
    }

    //    ... rest of your drawing code here ...
    //    don't forget to draw focus and push states too ;)

}

附录:

我无法相信我第一次错过了这个,但是你对SubclassDlgItem的呼唤可能没有达到预期的效果。此调用首先使控件父窗口处理用于按钮的消息。因为DrawItem(CDialog的超类)中的CWnd的默认实现不会对消息传递给控件。

将此替换为以下代码段,一切正常:

HWND hWndButton;
GetDlgItem(IDC_HEAD, &hWndButton);
CBipedHead.SubclassWindow(hWndButton);

这里有两个旁注:

  1. 对类和类成员使用相同的命名约定通常不是一个好主意。这让人感到困惑。
  2. 我猜你总是在发布模式下编译和运行。如果你是 - 不要。这可以防止抛出断言并让你知道出错了。

答案 1 :(得分:1)

不是 答案,而是 答案:这个自定义CCheckBox我发现了或多或少能够实现我想要的。默认情况下,它不允许3种状态,但我通过一些自己的调整来解决这个问题。我不是100%确定它是开箱即用的(我有一些问题,因为我的编辑没有似乎,但我不能确定),但是这是我用过的解决方案。不过,我不会把这个称为答案,以防万一有人可以窥探我的代码有什么问题,并想照亮我。