3

不確定状態に別のビットマップを使用するための3状態チェックボックスを取得するにはどうすればよいですか?

スリーステートチェックボックスで使用されている画像を別の画像に変更したい。コントロールはWin98スタイルであり、そのようなチェックボックスの不確定な状態を無効なチェックボックスと区別するのは困難です(これが、WinXPスタイルのコントロールでこれを変更した理由と思われますが、プロジェクトの他の詳細のために使用できません) 。

Visual C ++ 2010を使用しており、VSのリソースエディターで8x8ビットマップを定義しました。ビットマップのIDはIDB_INDET_CHECKです。

このようなものの標準的な「技術」が何であるかは完全にはわかりません。私は本当にWindowsコントロールとMFCの操作を始めたばかりです。

私の最初の試みは、CTriButtonから派生したクラスを作成しCButton、関数をオーバーライドして、DrawItem自分で描画しようとすることでした。次にSubclassDlgItem、ウィンドウのチェックボックスの1つをこのクラスに変えていました(私は思いますか?)。これは…ある種の作品?チェックボックスが表示されなくなり、本来あるべき場所をクリックすると、空のチェックボックスフレームが表示されますが、それ以外は何も起こりません(コード内のデバッグメッセージが送信されません)。

これが関連するコードですが、これが正しいかどうはわかりません。まず、私のウィンドウのからのコード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);
}
4

2 に答える 2

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はおそらく望ましい効果をもたらしていません。この呼び出しにより、ボタン宛てのメッセージが最初にコントロールの親ウィンドウによって処理されます。DrawItemin (CDialogのスーパークラス)のデフォルトの実装CWndは何も行わないため、メッセージがコントロールに渡されることはありません。

これを次のスニペットに置き換えれば、すべて問題ないはずです。

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

ここに2つのサイドノート:

  1. 通常、クラスとクラスメンバーの両方に同じ命名規則を使用することはお勧めできません。それは紛らわしい読み取りになります。
  2. あなたはいつもリリースモードでコンパイルして実行していると思います。もしそうなら-しないでください。これにより、アサーションがスローされて、何かが間違っていることを通知するのを防ぎます。
于 2011-07-17T17:21:32.950 に答える
1

答えではなく、答えです。私が見つけたこの習慣はCCheckBox多かれ少なかれ私が望むものを可能にします。デフォルトでは3つの状態は許可されていませんが、私はいくつかの調整を加えて修正しました。箱から出してすぐに機能するかどうかは100%わかりません(編集によるものではないように思われる問題がいくつかありましたが、確信はありません)が、それは私が解決した解決策でした使用済み。ただし、誰かが私のコードのどこが悪いのかをスパイして、私を照らしたい場合に備えて、これを答えとは呼びません。

于 2011-07-24T04:55:05.290 に答える