c++visual-studiomfccmfctoolbar

CMFCToolbar treat toolbar buttons as a group (Check/Uncheck)


I have three buttons in a CMFCToolbar that is contained in a CDialogEx, that I have set their styles to TBBS_CHECKGROUP

This allows each button to be toggled from checked to unchecked states, however I'd like to treat these three buttons as a group and ONLY allow one button to be checked at a time.

I cannot find any way to achieve this.

Only one of the buttons below should be checked at a time. How can I ensure that the other buttons automaticaly are unchecked?

enter image description here

In my code:

DWORD dwCtrlStyle = TBSTYLE_FLAT | TBSTYLE_TOOLTIPS | CBRS_SIZE_DYNAMIC;
DWORD dwStyle = AFX_DEFAULT_TOOLBAR_STYLE;
if (m_ToolBar.CreateEx(this, dwCtrlStyle,
dwStyle, CRect(1, 1, 1, 1), IDR_TOOLBAR1_PNG))
{
    dwStyle = CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC;
    m_ToolBar.SetPaneStyle(m_ToolBar.GetPaneStyle() | dwStyle);
}

CMFCToolBarInfo info;

m_ToolBar.SetSizes(CSize(32, 32), CSize(32, 32));
BOOL ret2 = m_ToolBar.LoadBitmap(IDR_TOOLBAR1_PNG); // << THIS WORKS

CMFCToolBarButton but1(0, 0, L"HELLO", TRUE, 0);
CMFCToolBarButton but2(1, 1, L"HELLO", TRUE, 0);
CMFCToolBarButton but3(2, 2, L"HELLO", TRUE, 0);

but1.SetStyle(but1.m_nStyle | TBBS_CHECKGROUP);
but2.SetStyle(but2.m_nStyle | TBBS_CHECKGROUP);
but3.SetStyle(but3.m_nStyle | TBBS_CHECKGROUP);
m_ToolBar.InsertButton(but1, 0);
m_ToolBar.InsertButton(but2, 0);
m_ToolBar.InsertButton(but3, 0);
m_ToolBar.SetToolBarBtnText(0, _T("By"));
m_ToolBar.SetToolBarBtnText(1, _T("Your"));
m_ToolBar.SetToolBarBtnText(2, _T("Command"));
m_ToolBar.InsertSeparator(2);

m_ToolBar.SetWindowPos(0, 0, 0, 400, 36, 0, 0);

UPDATE:

I've been able to get a partial workaround by adding ON_UPDATE_COMMAND_UI_RANGE and using the following code (Although this allows me to ensure the other buttons are always unchecked, I'm still unable to change the state of the current button being clicked to keep it checked:

void CMFCApplication1Dlg::OnUpdateViewFilterData(CCmdUI* pCmdUI)
{
    for (int i = 0; i < 3; ++i)
    {
    
        UINT nID;
        UINT info;
        int img;
        m_ToolBar.GetButtonInfo(i, nID, info,img);
        int check = 2;
    
        if (nID != pCmdUI->m_nID)
        {
            UINT newStyle = m_ToolBar.GetButtonStyle(i) & ~(TBBS_CHECKED | TBBS_INDETERMINATE);
            if (check == 1)
                newStyle |= TBBS_CHECKED;
            else if (check == 2)
                newStyle |= TBBS_INDETERMINATE;
            m_ToolBar.SetButtonStyle(i, newStyle | TBBS_CHECKBOX);
        }

        if (nID == pCmdUI->m_nID)
        {
            // If the button that is clicked keep it selected
            //pCmdUI->SetCheck(1) // << Does not work
        }
    }
}

Solution

  • OK after much trial and error and a lot of coffee I have now established why CMFCToolbar fails to work correctly when in a CDialogEx.

    I had noticed that even though I had implemented ON_UPDATE_COMMAND_UI for each of the buttons, any attempt to use pCmdUI->SetCheck() or pCmdUI->Enable() would always just silently fail.

    It turns out that for these commands to work correctly in a CDialogEx the important missing piece of the puzzle is to ensure you set m_ToolBar.SetRouteCommandsViaFrame(FALSE);

    And then finally to ensure that the update command is called at the very beginning to set the initial check box states just add the following:

    ON_MESSAGE_VOID(WM_KICKIDLE, OnKickIdle)
    
    void CMFCApplication1Dlg::OnKickIdle()
    {
        if (IsWindowVisible())
        {
            CFrameWnd* pParent = (CFrameWnd*)this;
            if (pParent)
                m_ToolBar.OnUpdateCmdUI(pParent, (BOOL)FALSE);
        }
    }