如何使 CMFCToolBarComboBoxButton 能够在垂直模式下工作?

How to make CMFCToolBarComboBoxButton able to works in vertical mode?

MFC Feature Pack 工具栏组合按钮 (class CMFCToolBarComboBoxButton) 在水平工具栏模式下工作完美。但在垂直布局模式下,它是没有组合框功能的简单按钮。

如何使CMFCToolBarComboBoxButton能够在垂直模式下工作?

这是我的解决方案。我在垂直模式下覆盖了按钮的行为。现在按钮在垂直模式下按下时显示组合框下拉 window。

当您将组合框按钮添加到工具栏时,您应该在 ReplaceButton() 方法中使用 class CVerticalableToolBarComboBoxButton 而不是 CMFCToolBarComboBoxButton。

示例:在下图中,您可以看到按钮在水平和垂直模式下的行为。

水平模式

垂直模式

Class代码:

class CVerticalableToolBarComboBoxButton
    : public CMFCToolBarComboBoxButton
{
    DECLARE_SERIAL(CVerticalableToolBarComboBoxButton)

public:
    typedef CMFCToolBarComboBoxButton TBase;

protected:

    bool                m_bDoVerticalMode;

public:
    CVerticalableToolBarComboBoxButton(bool bDoVerticalMode = true);
    CVerticalableToolBarComboBoxButton(UINT uiID, int iImage, DWORD dwStyle = CBS_DROPDOWNLIST, int iWidth = 0, bool bDoVerticalMode = true);

    virtual ~CVerticalableToolBarComboBoxButton();

    virtual void Serialize(CArchive& ar);

    virtual BOOL OnClick(CWnd* pWnd, BOOL bDelay = TRUE);
    virtual void OnChangeParentWnd(CWnd* pWndParent);
    virtual void OnMove();
    virtual void OnSize(int iSize);

protected:
    void AdjustVerticalRect();
};


/////////////////////////////////////////////////////////////////////////////
// CVerticalableToolBarComboBoxButton
IMPLEMENT_SERIAL(CVerticalableToolBarComboBoxButton, CVerticalableToolBarComboBoxButton::TBase, VERSIONABLE_SCHEMA | 1)

    CVerticalableToolBarComboBoxButton::CVerticalableToolBarComboBoxButton(bool bDoVerticalMode /*= true*/)
    : m_bDoVerticalMode(bDoVerticalMode)
{}

CVerticalableToolBarComboBoxButton::CVerticalableToolBarComboBoxButton(UINT uiID, int iImage, DWORD dwStyle /*= CBS_DROPDOWNLIST*/, int iWidth /*= 0*/, bool bDoVerticalMode /*= true*/)
    : TBase(uiID, iImage, dwStyle, iWidth)
    , m_bDoVerticalMode(bDoVerticalMode)
{}

CVerticalableToolBarComboBoxButton::~CVerticalableToolBarComboBoxButton()
{}

void CVerticalableToolBarComboBoxButton::Serialize(CArchive& ar)
{
    TBase::Serialize(ar);

    if (ar.IsLoading()) {
        ar >> m_bDoVerticalMode;
    }
    else {
        ar << m_bDoVerticalMode;
    }
}

BOOL CVerticalableToolBarComboBoxButton::OnClick(CWnd* pWnd, BOOL bDelay /*= TRUE*/)
{
    BOOL bRes = FALSE;

    bool bDefault = m_bHorz || !m_bDoVerticalMode;
    if (!bDefault) {
        if (IsFlatMode()) {

            if (m_pWndEdit == NULL) {
                m_pWndCombo->SetFocus();
            }
            else {
                m_pWndEdit->SetFocus();
            }

            m_pWndCombo->ShowDropDown();

            if (pWnd != NULL) {
                pWnd->InvalidateRect(m_rectCombo);
            }

            bRes = TRUE;
        }
    }

    if (bDefault) {
        bRes = TBase::OnClick(pWnd, bDelay);
    }

    return bRes;
}

void CVerticalableToolBarComboBoxButton::OnChangeParentWnd(CWnd* pWndParent)
{
    TBase::OnChangeParentWnd(pWndParent);

    if (!m_bHorz & m_bDoVerticalMode) {
        AdjustVerticalRect();
    }
}

void CVerticalableToolBarComboBoxButton::OnMove()
{
    TBase::OnMove();

    if (!m_bHorz & m_bDoVerticalMode) {
        AdjustVerticalRect();
    }
}

void CVerticalableToolBarComboBoxButton::OnSize(int iSize)
{
    TBase::OnSize(iSize);

    if (!m_bHorz & m_bDoVerticalMode) {
        AdjustVerticalRect();
    }
}

void CVerticalableToolBarComboBoxButton::AdjustVerticalRect()
{
    ASSERT(m_bDoVerticalMode);
    ASSERT(!m_bHorz);

    if (m_pWndCombo->GetSafeHwnd() == NULL || m_rect.IsRectEmpty()) {

        m_rectCombo.SetRectEmpty();
        m_rectButton.SetRectEmpty();
        return;
    }

    CMFCToolBar* pParentBar = nullptr;
    {
        CWnd* pNextBar = m_pWndCombo->GetParent();
        while (pParentBar == nullptr && pNextBar != nullptr) {
            pParentBar = DYNAMIC_DOWNCAST(CMFCToolBar, pNextBar);
            pNextBar = pNextBar->GetParent();
        }
    }

    if (IsCenterVert() && (!m_bTextBelow || m_strText.IsEmpty()) && (pParentBar != nullptr))
    {
        const int nRowHeight = pParentBar->GetRowHeight();
        const int yOffset = std::max<>(0, (nRowHeight - m_rect.Height()) / 2);

        m_rect.OffsetRect(0, yOffset);
    }

{
    CRect rect;
    m_pWndCombo->GetWindowRect(&rect);
    const int nWidth = std::max<>(rect.Width(), m_iWidth);

    rect.left = m_rect.left;
    rect.top = m_rect.top;
    rect.right = m_rect.left + nWidth;
    rect.bottom = m_rect.top + m_nDropDownHeight;

    if ((pParentBar != nullptr) && pParentBar->IsDocked()) {
        const UINT nID = pParentBar->GetParentDockSite()->GetDockSiteID();
        if (nID == AFX_IDW_DOCKBAR_RIGHT) {
            rect.left = m_rect.right - nWidth;
            rect.right = m_rect.right;
        }
    }

    m_pWndCombo->SetWindowPos(NULL, rect.left, rect.top, rect.Width(), rect.Height(), SWP_NOZORDER | SWP_NOACTIVATE);
    m_pWndCombo->SetEditSel(-1, 0);
}

{
    m_pWndCombo->GetWindowRect(&m_rectCombo);
    m_pWndCombo->ScreenToClient(&m_rectCombo);
    m_pWndCombo->MapWindowPoints(m_pWndCombo->GetParent(), &m_rectCombo);

}

if (m_bFlat) {
    m_rectButton = m_rectCombo;
}
else {
    m_rectButton.SetRectEmpty();
}
}

// CVerticalableToolBarComboBoxButton
/////////////////////////////////////////////////////////////////////////////