MFC Allow use to enable/disable floating toolbar via menu command

760 views Asked by At

Added Update 2 on 3/29/2022

My MFC app has the menubar, toolbar, and status bar all working correctly. I am at the point I'm adding creature features...one that really annoys me is the choice of permanently locking them or allow them to float at runtime...I've looked high and low and I've yet to see an example where a user can allow this feature to be dynamically changed once the app is up and running. The first function is from the app which works correctly. NOTE: I have the items allowing docking commented out as this was my test code..I know that those are the lines that enable or disable docking at run time....-> // enable docking

    int CMainFrame::OnCreate(LPCREATESTRUCT pptCreate)
{
    CMFCVisualManager::SetDefaultManager(RUNTIME_CLASS(CMFCVisualManagerVS2005));
    if ( -1 == CMDIFrameWndEx::OnCreate(pptCreate) )
        return -1;

    // create menu bar
    if ( !m_wndMenuBar.Create(this) )
        return -1;  
    m_wndMenuBar.SetPaneStyle(m_wndMenuBar.GetPaneStyle() | CBRS_SIZE_DYNAMIC | CBRS_TOOLTIPS | CBRS_FLYBY);
    
    // prevent the menu bar from taking the focus on activation
    CMFCPopupMenu::SetForceMenuFocus(FALSE);
    
    // set the visual manager and style based on persisted value
    theApp.m_nAppLook = theApp.GetInt(_T("ApplicationLook"), ID_VIEW_APPLOOK_VS_2008);
    OnApplicationLook(theApp.m_nAppLook);

    // create tool bar
    if ( !m_wndToolBar.CreateEx(this, TBSTYLE_FLAT, WS_CHILD|WS_VISIBLE|CBRS_TOP|CBRS_GRIPPER|CBRS_TOOLTIPS|CBRS_FLYBY|CBRS_SIZE_DYNAMIC) || !m_wndToolBar.LoadToolBar(IDR_MAINFRAME) )
        return -1;

    // create status bar
    if ( !m_wndStatusBar.Create(this) || !m_wndStatusBar.SetIndicators(miIndicators, sizeof(miIndicators)/sizeof(UINT)) )
        return -1;

    // enable docking
    //m_wndMenuBar.EnableDocking(CBRS_ALIGN_ANY);
    //m_wndToolBar.EnableDocking(CBRS_ALIGN_ANY);
    m_wndToolBar.EnableToolTips(TRUE);
    EnableDocking(CBRS_ALIGN_ANY);
    DockPane(&m_wndMenuBar);
    DockPane(&m_wndToolBar);

    // enable Visual Studio 2005 style docking window behavior
    CDockingManager::SetDockingMode(DT_SMART);
    // enable Visual Studio 2005 style docking window auto-hide behavior
    EnableAutoHidePanes(CBRS_ALIGN_ANY);

    // create docking windows
    if (!CreateDockingWindows())
    {
        TRACE0("Failed to create docking windows\n");
        return -1;
    }

    m_wndOutput.EnableDocking(CBRS_ALIGN_ANY);
    DockPane(&m_wndOutput);

    return 0;
}

So I've created a menu item that toggles a flag to turn "docking" on and off. The variable switches correctly.. but the command execution of turning the dock "3 dots" on the left side of the menubar and toolbar always remain...i.e. never goes from Float to Dock mode and vice versa. The logic of the toggle works as I can see in my debugging status window the entry points of TRUE and FALSE are seen each time I click the menu. So I guess the question is, can these items be dynamically turned from FLOAT to DOCK without destroying the window and trying to reinit it which would be a visual disaster?

void CMainFrame::UserDockingBarsOption()
{
    CMainFrame* pMainFrame = (CMainFrame*)AfxGetMainWnd();
    pMainFrame->m_wndOutput.AddStringDebugTab(_T("Debug: MainFrame--CMainFrame::UserDockingBarsOption()"));

    if (UserDockingFlag == TRUE)

    {   
        m_wndMenuBar.EnableDocking(CBRS_ALIGN_ANY);
        m_wndToolBar.EnableDocking(CBRS_ALIGN_ANY);
        EnableDocking(CBRS_ALIGN_ANY);
        UserDockingFlag = FALSE;
        pMainFrame->m_wndOutput.AddStringDebugTab(_T("Debug: MainFrame--Inside True"));
    }
    else
    {
        m_wndMenuBar.EnableDocking(FALSE);
        m_wndToolBar.EnableDocking(FALSE);
        EnableDocking(FALSE);
        UserDockingFlag = TRUE;
        pMainFrame->m_wndOutput.AddStringDebugTab(_T("Debug: MainFrame--Inside False"));
    }
}

Update 1:

So like my other posts, I'm back after a family matter. I revisited this and because I'm using:

CMFCMenuBar m_wndMenuBar;
CMFCToolBar m_wndToolBar;

and the only member I can find is IsFloat as a question, not an execute. So it was mentioned I can float or dock it via:

https://learn.microsoft.com/en-us/cpp/mfc/docking-and-floating-toolbars?view=msvc-160

Every example that I see is done within the OnCreate which does not help me as I can't recall it after it's already the child is already constructed. So my thought was to invalidate the toolbar, destroy the toolbar, recreate the tool bar with the user switch of dock or float, and then reorganize the toolbars. I have "most" of this working except for the actual switch control aspect to rebuild the menu with the user option of floating the toolbar or docking it.

What I did in my property sheet is created two buttons for two seperate functions to see if I can get each piece of this to work. One button is destroy the toolbar, the other button is create i.e. recreate the toobar....and both of those "work".

This is all being done in the MainFrame.cpp for simplicity of the member variables, I may move it later if I can get it working.

There are the two functions:

Destroy Function:

void CMainFrame::OnToolBarDestroy()
{
    
    m_wndToolBar.Invalidate();
    m_wndToolBar.DestroyWindow();
    RecalcLayout();
    //OnToolBarCreate();

    //m_wndToolBar.ShowPane(FALSE, FALSE, FALSE); // Hide toolbar
}

Recreate Toolbar outside of OnCreate

    int CMainFrame::OnToolBarCreate()
{
    // TODO: Add your implementation code here.

    if (m_wndToolBar)
    {
        m_wndOutput.AddStringStatusTab(_T("Error: Icon toolbar is already active, action cancelled"));
        m_wndOutput.AddStringDebugTab(_T("Debug: MainFrame--Error: Icon toolbar is already active, action cancelled"));
        return -1;
    }

    // Create ToolBar toolbar
    if (!m_wndToolBar.CreateEx(this, TBSTYLE_FLAT, WS_CHILD | WS_VISIBLE | CBRS_TOP
        | CBRS_GRIPPER | CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC) ||
        !m_wndToolBar.LoadToolBar(IDR_MAINFRAME))
    {
        TRACE0("Failed to Create Dialog ToolBar\n");
        return -1;
    }

    CRect rcClientOld;
    CRect rcClientNew;

    GetClientRect(rcClientOld);
    RepositionBars(AFX_IDW_CONTROLBAR_FIRST, AFX_IDW_CONTROLBAR_LAST, 0, reposQuery, rcClientNew);

    //m_wndToolBar.EnableDocking(CBRS_ALIGN_ANY);
    //DockPane(&m_wndToolBar);

    RecalcLayout();
    //m_wndToolBar.ShowPane(TRUE, FALSE, FALSE); // Show toolbar
    return -1;

}

So what is commented out that would make it "work" assuming following the OnCreate code order up to this point:

//m_wndToolBar.EnableDocking(CBRS_ALIGN_ANY);

//DockPane(&m_wndToolBar);

What happening is the successful destroy, and successful rebuild of the menu.

What I need for switch control is to either have the line active for floating or commented out for docked which I have also done in OnCreate but since this is for the rebuilt menu, I need this to follow that method switching in or out this line: //m_wndToolBar.EnableDocking(CBRS_ALIGN_ANY); ...but when I do....the toolbar gets all garbled up / missing all icons and I have to nuke the registry keys for the workspace to "try again"...

Any ideas on how I can make this work? I feel like I'm This close to getting it...I'm just unsure what I may be missing here to get across he goal line.

Update 2

So I got the toolbar to come out of docked to float with modified code below without restarting the app. But there are issues, it seems to be drawing "ghost" bars underneath. If I double click the 3 dots, it will detach and the bar will float....GREAT! But, it leave a "mirror" behind....if I double click the floating menu, it redocks to the frame.

I know I'm close, I'm just missing a piece to finish this off. I've added the code below and a few screenshots of wha I'm seeing.....the point is I is "working", but I'm missing something...can anyone help?

OnDestroy method updated:

    int CMainFrame::OnToolBarDestroy()
{
    
    //if (!m_wndToolBar)
    //{
    //  m_wndOutput.AddStringStatusTab(_T("Error: Icon toolbar is already removed, action cancelled"));
    //  m_wndOutput.AddStringDebugTab(_T("Debug: MainFrame--Error: Icon toolbar is already removed, action cancelled"));
    //  return -1;
    //}

    //m_wndToolBar.Invalidate();
    //m_wndMenuBar.DestroyWindow();
    m_wndToolBar.DestroyWindow();
    //m_wndToolBar.AdjustDockingLayout();
    //RecalcLayout();
    //OnToolBarCreate();
    
    //return 0;

    CRect rcClientOld;
    CRect rcClientNew;
    GetClientRect(rcClientOld);
    RepositionBars(AFX_IDW_CONTROLBAR_FIRST, AFX_IDW_CONTROLBAR_LAST, 0, reposQuery, rcClientNew);

    //m_wndToolBar.ShowPane(FALSE, FALSE, TRUE); // Hide toolbar
    RecalcLayout();
    return 0;
}

OnCreate method updated:

    int CMainFrame::OnToolBarCreate()
{
    enter code here// TODO: Add your implementation code here.

    if (m_wndToolBar)
    {
        m_wndOutput.AddStringStatusTab(_T("Error: Icon toolbar is already active, action cancelled"));
        m_wndOutput.AddStringDebugTab(_T("Debug: MainFrame--Error: Icon toolbar is already active, action cancelled"));
        return -1;
    }

    CMFCPopupMenu::SetForceMenuFocus(FALSE);
    //CMDIChildWndEx::m_bEnableFloatingBars = TRUE;

    // Create ToolBar toolbar
    if (!m_wndToolBar.CreateEx(this, TBSTYLE_FLAT, WS_CHILD | WS_VISIBLE | CBRS_ALIGN_TOP
        | CBRS_GRIPPER | CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC) ||
        !m_wndToolBar.LoadToolBar(IDR_MAINFRAME))
    {
        TRACE0("Failed to Create Dialog ToolBar\n");
        return -1;
    }


    m_wndMenuBar.EnableDocking(FALSE);
    m_wndToolBar.EnableDocking(FALSE);
    m_wndMenuBar.EnableDocking(CBRS_ALIGN_ANY);
    m_wndToolBar.EnableDocking(CBRS_ALIGN_ANY);
    EnableDocking(CBRS_ALIGN_ANY);
    DockPane(&m_wndMenuBar);
    DockPane(&m_wndToolBar);
    //  DockPaneLeftOf(&m_wndToolBar);
    CDockingManager::SetDockingMode(DT_SMART);
    EnableAutoHidePanes(CBRS_ALIGN_ANY);

    CRect rcClientOld;
    CRect rcClientNew;
    GetClientRect(rcClientOld);
    RepositionBars(AFX_IDW_CONTROLBAR_FIRST, AFX_IDW_CONTROLBAR_LAST, 0, reposQuery, rcClientNew);

    m_wndOutput.AddStringStatusTab(_T("I'm created 1 times"));

    //m_wndToolBar.ResetAll();

    RecalcLayout();

    //m_wndToolBar.ShowPane(TRUE, FALSE, TRUE); // Show toolbar
    return 0;

}

The images below:

  1. Start from OnCreate with the panes properly "docked" mode
  2. Destroyed ToolBar
  3. ToolBar is switched from dock to float mode with Update 2 code. Ghost bars shown in the image. Trying to solve that issue.

First

Second

Third

Soooooo....anyone have any ideas on how I can solve this riddle?

Thanks! Chris

0

There are 0 answers