Click here to Skip to main content
15,891,828 members
Articles / Desktop Programming / MFC
Article

Disabling the close button on a floating toolbar

Rate me:
Please Sign up or sign in to vote.
4.48/5 (16 votes)
10 Apr 2004CPOL4 min read 116.6K   3K   29   11
How to disable the close button on a floating toolbar

Introduction

I wanted a floating toolbar in my application in addition to the standard docked toolbar. The floating toolbar isn't dockable. Thus it always looks like this:

See that little 'x' in the top right corner? All I wanted was to disable it so that the user couldn't dismiss the toolbar. As you'd guess, it turned out to not be terribly obvious how to disable it.

Background

There are a number of variations on the Toolbar in MFC. We have CToolBar, CDialogBar and so on. I chose CToolBar as my base class. In MFC SDI applications you create your toolbars in the MainFrame's OnCreate() member function and do the appropriate voodoo to dock or float them. In my application it looks like this.
C++
if (!m_wndToolBar.CreateEx(this) || !m_wndToolBar.LoadToolBar(IDR_TOOLBAR))
{
    TRACE0("Failed to create toolbar\n");
    return -1;      // fail to create
}

CPoint pt(0, 25);

ClientToScreen(&pt);
m_wndToolBar.EnableDocking(0);
FloatControlBar(&m_wndToolBar, pt);
which creates the toolbar, disables docking for that particular toolbar and calls FloatControlBar() specifying the initial screen coordinates for the toolbar.

We know that the window handle exposed in our CToolBar object is a handle to a Win32 Toolbar Control and we also know that such a control doesn't have a caption, a thick frame or a system menu/close button. Obviously, then, our Toolbar Control is hosted within another window.

Digging around in the window heirarchy using Spy++ reveals that the window handle contained in the m_wndToolBar object is a child of an AfxControlBar (the exact class name varies) which in turn is a child of a top level window.

Time to turn to the MFC source code.

Cutting a long story short, after creating the Toolbar Control Window you choose either to dock it or float it. In either case an AfxControlBar is created and made the parent of our toolbar. Then, if you're docking the window, the AfxControlBar is made a child of your MainFrame window. If, however, you're floating the toolbar, another window is created to parent the AfxControlBar. That other window is a CMiniDockFrameWnd and that's the window which has the close button.

CMiniDockFrameWnd

This is an undocumented helper class in MFC derived from CMiniFrameWnd. The framework creates a CMiniDockFrameWnd whenever it needs one and does the appropriate magic to wire our Toolbar Control to it, and to wire it to our MainFrame window.

The standard approach to this problem might be to derive our own class from CMiniDockFrameWnd either substituting a new message handler for the close button or modifying the window styles at creation time to prevent the close button even appearing.

Unfortunately this approach won't work. The problem is there's no way to derive a new class from CMiniFrameWnd and get the framework to use that class unless you're prepared to create your own private version of MFC. In particular, neither EnableDocking() nor FloatControlBar() are virtual so you can't replace that small portion of the framework with an override that would create your derivative of CMiniDockFrameWnd. Dunno about you but there's no way I'm prepared to create my own private version of MFC. Nor, for that matter, am I prepared to cut and paste a significant proportion of MFC into my own class definitions to allow the creation and use of a derivation of CMiniDockFrameWnd.

So we have to find another way. How about subclassing the CMiniDockFrameWnd? Nope, that won't work either. The window is already subclassed by MFC and you'll hit ASSERTs if you try. (I'm talking about MFC style sub-classing here. Direct subclassing may work - I didn't try it.)

My final solution

is maybe somewhat 'quick-and-dirty' but it works quite well. The solution is to navigate up the window heirarchy from the ToolBar Control Window to the CMiniDockFrameWnd, get the system menu and disable the 'Close' item. This disables the close button. I also remove the 'Close' item from the menu. The latter is done because you can right click on the toolbar caption and get access to the system menu. The code looks like this.
C++
void CMyToolBar::DisableCloseButton()
{
    //  Now remove the Hide menuitem so obligingly
    //  installed for us by the CMiniDockFrameWnd
    //  class.  We do this by navigating 2 levels
    //  up from our toolbar to the enclosing
    //  frame window and modifying its system menu.
    CWnd *pWnd = GetParent();

    if (pWnd != (CWnd *) NULL)
    {
        ASSERT_KINDOF(CWnd, pWnd);
        pWnd = pWnd->GetParent();
    }

    if (pWnd != (CWnd *) NULL)
    {
        ASSERT_KINDOF(CWnd, pWnd);

        //  Make sure the window we found isn't our MainFrame
        //  window.
        if (pWnd->GetSafeHwnd() != AfxGetMainWnd()->GetSafeHwnd())
        {
            CMenu *pSysMenu = pWnd->GetSystemMenu(FALSE);

            if (pSysMenu != (CMenu *) NULL)
            {
                ASSERT_KINDOF(CMenu, pSysMenu);

                pSysMenu->DeleteMenu(1, MF_BYPOSITION);
                pSysMenu->EnableMenuItem(SC_CLOSE, MF_BYCOMMAND | MF_DISABLED);
                pSysMenu->DeleteMenu(SC_CLOSE, MF_BYCOMMAND);
            }
        }
    }
}
The code is based on the assumption that there's a menu attached to the grandparent of our ToolBar window. That'll be true both for docked and floating windows in the current incarnation of MFC7. As a safety check we ensure that we're not modifying the system menu for our MainFrame. We also check the validity of our assumptions at each step by verifying that the pointers we get back are valid.

Using the code

Add the two files in the source download to your project. You probably want to give the class a better name than CMyToolBar. In your CMainFrm class change the CToolBar to be a CMyToolBar. Then, sometime early in the life of the Toolbar but after the MainFrame window has been created and after you've called the FloatControlBar() function you call the DisableCloseButton() function. The demo project shows how this is done. Voila!

Other notes

Interestingly enough, deep within the bowels of CMiniDockFrameWnd is some code to modify the system menu. The 'Close' menuitem is removed and added back in with 'Hide' as the text. The WM_CLOSE handler doesn't actually close a CMiniDockFrameWnd, it merely hides it. The Toolbar can be made visible again by calling CFrameWnd::ShowControlBar().

History

11 April 2004 - Initial version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
United States United States
I've been programming for 35 years - started in machine language on the National Semiconductor SC/MP chip, moved via the 8080 to the Z80 - graduated through HP Rocky Mountain Basic and HPL - then to C and C++ and now C#.

I used (30 or so years ago when I worked for Hewlett Packard) to repair HP Oscilloscopes and Spectrum Analysers - for a while there I was the one repairing DC to daylight SpecAns in the Asia Pacific area.

Afterward I was the fourth team member added to the Australia Post EPOS project at Unisys Australia. We grew to become an A$400 million project. I wrote a few device drivers for the project under Microsoft OS/2 v 1.3 - did hardware qualification and was part of the rollout team dealing directly with the customer.

Born and bred in Melbourne Australia, now living in Scottsdale Arizona USA, became a US Citizen on September 29th, 2006.

I work for a medical insurance broker, learning how to create ASP.NET websites in VB.Net and C#. It's all good.

Oh, I'm also a Kentucky Colonel. http://www.kycolonels.org

Comments and Discussions

 
QuestionWhat if the toolbar is made not floating then floating again... Pin
olivier gg29-Apr-09 1:43
olivier gg29-Apr-09 1:43 
GeneralDouble click on title bar... Pin
Ali Khanlarkhani10-Jun-07 20:48
Ali Khanlarkhani10-Jun-07 20:48 
GeneralOverride CalcFixedLayout Pin
Tiefeng You25-Aug-05 8:46
Tiefeng You25-Aug-05 8:46 
GeneralRe: Override CalcFixedLayout Pin
Seg@15-Sep-08 5:32
Seg@15-Sep-08 5:32 
GeneralCMiniDockFrameWnd derived Pin
Gérard Brouwers18-May-05 20:59
Gérard Brouwers18-May-05 20:59 
GeneralThis should also work Pin
Steve Mayfield31-Aug-04 15:05
Steve Mayfield31-Aug-04 15:05 
GeneralRe: This should also work Pin
wombat26-Sep-06 22:00
wombat26-Sep-06 22:00 
GeneralRe: This should also work Pin
wombat27-Sep-06 1:41
wombat27-Sep-06 1:41 
GeneralIE6 style image hover toolbar Pin
addinitz6-May-04 9:58
addinitz6-May-04 9:58 
GeneralRe: IE6 style image hover toolbar Pin
Rob Manderson6-May-04 10:06
protectorRob Manderson6-May-04 10:06 
GeneralHELP; its not working Pin
suneetchandok11-Apr-04 21:03
susssuneetchandok11-Apr-04 21:03 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.