DataMekanix Home Docking Windows Made Easy


home / CSizingControlBar / manual


 

Note: I will add more info in the near future on this page, so check it often.

Getting Started

1. Include the following files in your project:

sizecbar.h
sizecbar.cpp
scbarg.h
scbarg.cpp

2. Add these lines to your stdafx.h (if the files are in a different directory, include the path - see the stdafx.h file in the samples):

#include "sizecbar.h"
#include "scbarg.h"

3. Derive a class from CSizingControlBarG (you have an example in mybar.* files).

4. In mainfrm.h, include your class' header:

#include "mybar.h"
then add a member variable to CMainFrame:
CMyBar m_wndMyBar;

5. Create the bar in CMainFrame::OnCreate(). Then set bar styles, enable it to dock... like any control bar.

if (!m_wndMyBar.Create(_T("My Bar"), this, 123)
{
    TRACE0("Failed to create mybar\n");
    return -1;
    // fail to create
}
m_wndMyBar.SetBarStyle(m_wndMyBar.GetBarStyle() |
    CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC);

m_wndMyBar.EnableDocking(CBRS_ALIGN_ANY);

EnableDocking(CBRS_ALIGN_ANY);

DockControlBar(&m_wndMyBar, AFX_IDW_DOCKBAR_LEFT);

Advanced Example

The instructions above will make a docking bar with a DevStudio-like gripper (with 2 raised lines and a hide button) when docked, and with no gripper when floating.

Let's explore some advanced features. Now we will use the CSizingControlBarCF class as a base class, and will hide the miniframe caption, showing the gripper in the floating state too. That's possible, because CSizingControlBarCF's gripper looks like a small caption. As a side effect of using a custom miniframe class, the resizing of the floating bar will be dynamic if "Show window contents while dragging" display property is enabled.

1. Add these files to your project too:

scbarcf.h
scbarcf.cpp

2. Change the stdafx.h file to look like this:

#define _SCB_REPLACE_MINIFRAME
#include "sizecbar.h"
#include "scbarg.h"
#include "scbarcf.h"
#define baseCMyBar CSizingControlBarCF

3. Add these lines in CMainFrame::OnCreate(), after the EnableDocking() call

#ifdef _SCB_REPLACE_MINIFRAME
    m_pFloatingFrameClass = RUNTIME_CLASS(CSCBMiniDockFrameWnd);
#endif //_SCB_REPLACE_MINIFRAME

Class Reference

CSizingControlBar

Base class: CControlBar
Location: sizecbar.h/.cpp

This class implements most of the resizable window features - layout management, edge tracking, state persistence, non-client area calculation and painting, etc.

Class members:

virtual BOOL Create(LPCTSTR lpszWindowName, CWnd* pParentWnd, UINT nID,
                    DWORD dwStyle = WS_CHILD|WS_VISIBLE|CBRS_TOP);

Creates a resizable control bar. Most of the parameters are the same as for the common MFC control bars.
lpszWindowName will be the window title, which is shown on the caption of the miniframe, and/or is painted on the gripper by derived classes like CSizingControlBarCF.
nID can be any valid window ID. It is strongly recommended to use different IDs for each bar, because the state persistence implementation is based on them.

virtual void LoadState(LPCTSTR lpszProfileName);

Used in conjunction with CFrameWnd::LoadBarState(), allows the bar to remember the previous sizes and positions in all states: floating, horizontally or vertically docked. The call loads from registry or from an .ini file the m_szFloat, m_szHorz and m_szVert members.
Best place to call: in CMainFrame::OnCreate(), just before LoadBarState().

virtual void SaveState(LPCTSTR lpszProfileName);
Saves the size members for later retrieval by LoadState().
Best place to call: in CMainFrame::OnClose(), or CMainFrame::DestroyWindow().
Warning: CFrameWnd::LoadBarState() doesn't check for validity of the information it retrieves, and will crash your app if older/invalid state info is loaded. Either use a different value for lpszProfileName in all state calls if you change/remove bar IDs in a new version of your app (but this will leave junk in the user's registry), or - better - implement a safer mechanism.
static void GlobalLoadState(CFrameWnd* pFrame, LPCTSTR lpszProfileName);
static void GlobalSaveState(CFrameWnd* pFrame, LPCTSTR lpszProfileName);
The same as LoadState()/SaveState(), but there is only one call needed to save all bars' state info. See the samples for details. The pFrame is a pointer to the hosting frame window (usually, this will be the main frame, so you will simply provide the this member).
const BOOL IsFloating() const;

Returns TRUE if the bar is floating, FALSE otherwise.

const BOOL IsHorzDocked() const;

Returns TRUE if the bar is docked at the top or bottom of the frame (it is horizontally oriented), FALSE otherwise.

const BOOL IsVertDocked() const;

Returns TRUE if the bar is docked on the lateral sides of the frame (left or right - vertically oriented), FALSE otherwise.

void SetSCBStyle(DWORD dwSCBStyle);

Sets the SCBS_* styles. It is strongly recommended to use this function before the bar is created. These styles are not related with window styles.
dwSCBStyle can be a combination of the following styles:

  • SCBS_SHOWEDGES - Enables sizing borders (look like splitter edges). The client area is shrinked to make room for these borders. This is only a cosmetic feature - the bar is resizable regardless this style is set or not.
  • SCBS_SIZECHILD - Enables automatically child resizing. If the bar has a child window, it will be resized automatically to cover the entire client area of the bar, so you don't need to handle WM_SIZE in a derived class. By setting this style you can obtain a simple resizing bar with all the default features, without the need of deriving a new class, but you can set this style for a custom derived class too. However, only a single child window is allowed for bars having this style.
  • There are more styles used internally by the bar, like SCBS_EDGE* styles. You are not able to modify them using this function.
const DWORD GetSCBStyle() const;

Returns the SCBS_* style combination of the bar. See SetSCBStyle() above for a list of available styles.

CSizingControlBarG

Base class: CSizingControlBar
Location: scbarg.h/.cpp

Adds a gripper and a hide bar button in the non-client area. The non-client area is adjusted for the gripper in NcCalcClient() and painted in NcPaintGripper(). These 2 virtual member functions of CSizingControlBar are the framework for customizing the non-client area.

OnNcHitTest() and OnNcLButtonUp() are used to implement the functionality of the button, while OnUpdateCmdUI() updates its look (flat/pushed/raised).

Class members:

virtual BOOL HasGripper() const;

Call this function to find if the bar will display the gripper in the current state. By default it returns FALSE if the bar is floating and the miniframe has its own caption, and TRUE otherwise.

CSizingControlBarCF

Base class: CSizingControlBarG
Location: scbarcf.h/.cpp

Using this class as a base class causes a small caption to be drawn for the bar, with the window title. If the bar is docked horizontally, the caption is painted sideways, with the text rotated 90 degrees CCW. In fact, it is the gripper which is custom painted - the bar functionality is similar with CSizingControlBarG's (the base class).

A nice feature is child focus auto-sensing: if a child window of the bar has the focus, the caption is painted as active, giving a useful feedback to the user. Keep in mind that this is different from the MFC's active view concept: an active view will receive command messages and will translate keyboard accelerators even if the view doesn't have the focus. An active bar means just this - a window which is child of the bar has the keyboard focus.

All colors used are the system colors, so you don't need to worry if you app will look bad if the user has other settings for display appearance. Even more - support for gradient captions is enabled for VC++ 5.0 too, so you can use the compiler version of choice. The gradient captions are painted only if the user host system is Win98, Win2000, or above, and it is based (of course) on the system settings. More specific: if the main window caption is painted with gradient, the bars will have gradient captions too.


Remarks

These classes are intended to be used as base classes. Do not simply add your code to the files - instead create a new class derived from CSizingControlBarG or CSizingControlBarCF and put there what you need. If you want to customize your gripper, or simply don't want a gripper, you can use CSizingControlBar as a base class.

Window IDs: The usage of IDs in the range of AFX_IDW_CONTROLBAR_FIRST + 32 .. AFX_IDW_CONTROLBAR_LAST is required only if the bar will not be enabled for docking (that's is - it will stay fixed right under the frame's menu). But in this situation you won't be able to fully use the features of this class, so if you will enable it to dock (a reasonable guess :) then you can use any valid window ID.
Another place where the IDs are important is the saving/loading of the bar's state. You must use different IDs for each control bar that is enabled to dock, and this includes the other bars too. For example, if you have two toolbars, you can create the first one with the default ID (which is AFX_IDW_TOOLBAR = AFX_IDW_CONTROLBAR_FIRST), but the second one must have a different ID.

OnUpdateCmdUI: This member function is pure virtual in CControlBar (the base class of CSizingControlBar). Its purpose is to allow updating of controls at idle time (from here CCmdUI::DoUpdate() is called for the toolbars buttons, controls on dialog bars, panes of status bar, etc.).
However, I found it very useful to update the look of the "x" flat button in CSizingControlBarG and the color of the caption in CSizingControlBarCF (no timers needed). So, if you will use this function, don't forget to call the base class' member (see mybar.cpp).

Dynamic resizing: This feature allows redrawing of the bar during resizing. Also all the bars are repositioned and redrawn if necessary.
The SPI_GETDRAGFULLWINDOWS system parameter is queried for this (it is enabled by the "Show window contents while dragging" checkbox in Display Properties).

CBRS_SIZE_DYNAMIC: This bar style is required. Make sure you add it to the bar, otherwise the application will crash when the user floats a bar. You can add it using SetBarStyle() after Create(), or by changing the default style for Create() to something like: WS_VISIBLE|WS_CHILD|CBRS_TOP|CBRS_SIZE_DYNAMIC.

State persistence: The common MFC control bars' docking state is saved using CMainFrame::SaveBarState(). In addition to the info saved by this function, the CSizingControlBar class needs to save 3 sizes. This is done in CSizingControlBar::SaveState() function, so a m_wndMyBar.SaveState() call is required. Please note that the state storing code must be placed in CMainFrame's OnClose() or DestroyWindow(), not in OnDestroy(), because at the time the WM_DESTROY message is received, the floating bars are already destroyed.
In CMainFrame::OnCreate(), the m_wndMyBar.LoadState() call must be placed before LoadBarState().
Alternatively, if you have more than one resizable bars, you can call once the static member SizingControlBar::GlobalSaveState() instead of calling each bar's SaveState(). The same for LoadState() - there is a CSizingControlBar::GlobalLoadState() function. See both samples here for more details.

Precompiler flags: There are 2 symbols which can be defined to cause the floating bars to have different appearance and functionality:

  1. _SCB_REPLACE_MINIFRAME can be used to plug in CSCBMiniDockFrameWnd, which is a custom miniframe class. The main gain of using this class is that the floating bars can be resized dynamically, like all other windows. The other advantage is that the miniframe caption can be turned off, allowing the bar to display its own gripper, for increased functionality and/or custom designs.
    If you use this flag, you have to change the m_pFloatingFrameClass member of the main frame (see the advanced example above).
  2. _SCB_MINIFRAME_CAPTION can be defined only if the previous flag is also defined. It causes the custom miniframe to keep the tool window caption.
    Both CSizingControlBarG and CSizingControlBarCF classes do not display a gripper when floating if this flag is set.

The table below shows how the floating bar and the miniframe behave in response to these flags. When the bar is docked, the flags have no effect.

_SCB_REPLACE_MINIFRAME not defined not defined defined defined
_SCB_MINIFRAME_CAPTION not defined defined not defined defined
Miniframe class default - custom custom
Dynamic resizing No - Yes Yes
Miniframe caption Yes - No Yes
Bar gripper No - Yes No

See also the FAQ section for troubleshooting and more customization features.


Copyright 1998-2010 DataMekanix. All rights reserved.