DataMekanix Home Docking Windows Made Easy


home / CSizingControlBar / f.a.q.


 

Frequently Asked Questions (with answers :)

General control bars related questions
These issues are common to all MFC control bars (toolbars, dialog bars, even status bars). Since CSizingControlBar is CControlBar derived, they will apply to it too, as well as to any CControlBar derived implementation of docking windows.

CSizingControlBar specific

Miscellaneous

If you have more questions, post them in the message board.

 

Can I use views as children of control bars?

From the start I will say that using CView-based classes to implement children of control bars is not the best idea. The MFC framework for doc/view/frame is not that flexible. There are a lot of issues related to this, and if you are not a MFC guru, it is likely you will run into trouble.

There are cases when starting a new class from scratch is not desirable: if you already have a carefully crafted view class, or need functionality built in CView (like drag'n'drop, but with a little work you can add this functionality to a CWnd too), you can host views on the bars. See the Articles section for how to do this. Below are the reasons for which I gave you the warning:

As an exercise, please look after CWnd::GetParentFrame() implementation in the MFC sources (wincore.cpp) and hunt for the places it is called from.

A view has a close relationship with it's parent frame. The bad news is that the control bars can switch the parent frames: when a bar is docked, it is ultimately the child of CMainFrame (usually a CMDIFrameWnd for MDI or CFrameWnd for SDI). When it is floating, its parent frame is a CMiniDockFrameWnd, which in turn is child of the desktop (no parents).

The things get more complicated when a document is involved - OnCloseDocument() destroys the parent frames of all its views.

There are more like this, and I will not list them here. If you are curious, of if you are determined to put a view on a control bar, take a look at the articles in the Docking Windows section at CodeGuru, as well as at the ones in Advanced UI. You will find useful information if you also read the comments :)

Here are the main issues:

1.doc template/doc/parent/view frame relationship
2.activation/focus control
3.message routing/accelerators
4.creation/destruction

So, my advice is to use plain CWnd based controls where you can. Dialogs, toolbars(!), are good too.

 

When my bar is docked, the edges are not redrawn. Why?

Check the styles you passed to Create(). The default value is WS_CHILD|WS_VISIBLE|CBRS_TOP. It is very likely you forgot the CBRS_TOP style (you can also use CBRS_BORDERS_3D - see CControlBar::PreCreateWindow() in MFC sources).
Also verify the styles of other docking bars you may have (like toolbars). They must have this style passed to Create() too.

 

How do I convince my left bar to go down as far as the status bar (preventing the bar at the bottom from taking over the whole frame width)?

Replace the EnableDocking(CBRS_ALIGN_ANY); call from CMainFrame::OnCreate() with the following sequence:

    // EnableDocking(CBRS_ALIGN_ANY); // <-- comment this out
    EnableDocking(CBRS_ALIGN_TOP);
    EnableDocking(CBRS_ALIGN_LEFT);
    EnableDocking(CBRS_ALIGN_RIGHT);
    EnableDocking(CBRS_ALIGN_BOTTOM);
This is not directly related with CSizingControlBar - you can use this trick even for toolbars. The explanation is that the frame layout is done by asking all immediate children with IDs in the range AFX_IDW_CONTROLBAR_FIRST .. AFX_IDW_CONTROLBAR_LAST about the size it will ocupy. The order is the one in which they are created, every bar subtracting its area from the remaining client area. The default sequence for an AppWizard generated application is status bar, top, bottom, left, and right dockbars.
Just for fun: Move the Create() call for the status bar after the EnableDocking() call. Run your app and dock some bars at bottom and on sides - you will see the status bar in a strange location. OK, now revert the changes! :)
 

How to add a context menu to my bar?

You can do this in many ways. I will show here the simplest method.

First, you need a submenu in the main menu (the View submenu is great). Add there menu items of type ID_VIEW_MYBAR and add handlers in the mainframe (see both samples for this).

Now handle WM_CONTEXTMENU in your bar:

void CMyBar::OnContextMenu(CWnd* pWnd, CPoint point) 
{
    CMenu menu;
    menu.LoadMenu(IDR_MAINFRAME);

    menu.GetSubMenu(1)->TrackPopupMenu(TPM_LEFTALIGN,
        point.x, point.y, m_pDockSite);
}

The code above assumes you use the second submenu in IDR_MAINFRAME - this is the View menu for a default MDI AppWizard generated app.

Rebuild and enjoy!

 

When I drag my floating bar over other bars, it tryies to dock, but I want it floating at that position. Is this a bug?

No, it's the intended behavior. Just tell the user to press and hold down the Ctrl key while dragging the bar around - it will stay floating.

 

It is possible to disable floating so the bar will be always docked?

Yes, but the tradeoff is that you will have to disable dragging completely, so the user will no longer be able to drag the bar from one side to another.

For this, you will simply handle WM_LBUTTONDOWN and WM_LBUTTONDBLCLK in your class (like CMyBar) and do nothing in the handler implementations. This will prevent the bar to enter in the drag state.

Resizing the bar in place (where it is docked) is still possible, also the hide button functionality is preserved, because they are triggered by NC mouse messages.

 

It is possible to disable docking so the bar will be always floating?

This is simpler. In CMainFrame::OnCreate(), instead of this sequence:

    m_wndMyBar.EnableDocking(CBRS_ALIGN_ANY);
    DockControlBar(&m_wndMyBar, AFX_IDW_DOCKBAR_LEFT);

use this one:

    m_wndMyBar.EnableDocking(0); // no docking
    FloatControlBar(&m_wndMyBar, point);

Where point is the screen coordinate where you want the bar to be floated (if you use bar state persistence, the position and size are adjusted later).

 

How do I get a pointer to my bar/mainframe/view/etc.?

1. Pointer to mainframe (CMainFrame)
1.1. Generic (works from almost everywhere):

    CMainFrame* pMainFrame = (CMainFrame*) ::AfxGetMainWnd();

1.2. From inside CMyBar:

    CMainFrame* pMainFrame = (CMainFrame*) m_pDockSite;

2. Pointer to a controlbar (CMyBar)
2.1. Generic (ID_MYBAR here is the ID you passed to Create()):

    CControlBar* pBar = pMainFrame->GetControlBar(ID_MYBAR);
    CMyBar* pMyBar = (CMyBar*) pBar;

2.2. If you are in CMainFrame, you can use the member variable directly (like m_wndMyBar).
2.3. If you are in CMybar, use this :), If you are in a child of CMyBar, you can use GetParent() in the window hierarchy.

3. Pointer to a view
This depends a lot on the architecture you are using. If you are using doc/view, the safest way is to use CMainFrame members (like GetActiveDocument()) or CWinApp members to get a pointer to the document, then you can get the pointer to the view.
For SDI apps with a single view, you can use this:

    pMainFrame->GetDlgItem(AFX_IDW_PANE_FIRST);
 

How do I know if my bar is visible?

First, get a pointer to the bar (see this topic), then:

if (pMyBar->IsVisible()) ...
 

How do I show/hide my bar?

Get pointers to the bar and to the main frame (see this topic), then:

pMainFrame->ShowControlBar(pMyBar, TRUE);
pMainFrame->ShowControlBar(pMyBar, FALSE);

The code above shows/hides the bar immediatelly. If you want to show/hide multiple bars at once, you can pass TRUE for the optional bDelay parameter. The bars will be updated at the next idle time or specifically with a pMainFrame->RecalcLayout().

 

How to disable the mainframe's accelerators when the bar (or a child of the bar) has the focus?

Intercept PreTranslateMessage() somewhere in the pre-translation tree, then dispatch the keyboard messages. For example, this will work in many cases:

BOOL CMyBar::PreTranslateMessage(MSG* pMsg) 
{
    if (pMsg->message >= WM_KEYFIRST && pMsg->message <= WM_KEYLAST)
    {
        // dipatch keyboard messages directly (skip main accelerators)
        ::TranslateMessage(pMsg);
        ::DispatchMessage(pMsg);
        return TRUE;
    }

    return baseCMyBar::PreTranslateMessage(pMsg);
}
 

My child window is not redrawn properly when I resize the control bar. What should I do?

This was a bug in previous versions (earlier than v2.41), caused by a wrong combination of window styles for the bar. You should update to the latest version.
If the problem persists, you can change the code like below ( MyBar.cpp in the samples, CMyBar::OnSize() ) :

        m_wndChild.MoveWindow(rc);
        m_wndChild.Invalidate(); // <- Add this

or, better, replace the MoveWindow() call with SetWindowPos():

        m_wndChild.SetWindowPos(NULL,
            rc.left, rc.top,
            rc.Width(), rc.Height(),
            SWP_NOACTIVATE|SWP_NOZORDER|SWP_NOCOPYBITS);
 

I liked the raised borders from the previous versions.
Can I bring them back?

Yes. Add this line in your bar's constructor (CMyBar::CMyBar()):

    m_dwSCBStyle |= SCBS_SHOWEDGES;

Note you should do this for all the bars derived from CSizingControlBars, to achieve a consistent look.
The borders are turned off by default because I didn't find an easy, neat-looking solution for the transversal borders (the ones between 2 sizing bars docked on the same row, resembling a splitter). They work OK, it is just I don't like them :)

But, if you have only one sizing bar, you can turn them on as shown above, because there are no splitters. Also, even if you have more than one sizing bar, you can prevent them to dock on the same side by specifying different CBRS_ALIGN styles in the call to EnableDocking().

 

I want to do something when the bar is closing (the user clicks the "x" button).
How do I do this?

Don't know why you want to do this - it is simpler to check if the bar is visible or not.
You can catch WM_NCLBUTTONUP and test if nHitTest is HTCLOSE; you must call the base class handler. However, this will not announce you if the user hides the bar using the menu, or you if there is a ShowControlBar() called somewhere else in the code. Also, this doesn't work for floating bars with the default miniframe class.

A better method is the one suggested by R. van Meurs: catch WM_WINDOWPOSCHANGED in your bar class and look for either SWP_HIDEWINDOW or SWP_SHOWWINDOW in lpwndpos->flags. If one of these flags is present, the window is going to be hidden or shown.

 

I want to make my changes directly in sizecbar.h/.cpp files.
Is this okay with you?

I don't recommend it, but it's fine if you do it. Try to keep the changes to a minimum, and mark them so you know which is the original CSizingControlBar code and which is yours. This way you'll have an easier time tracking changes when a new version of this code is released, and it will make it a little bit more likely to get some support if you ask me a question.


Copyright 1998-2010 DataMekanix. All rights reserved.