Professional UI Solutions
Site Map   /  Register
 
 
 

Control Bars

How to set the exact initial positions of docked control bars when the application starts for the first time?

When your application starts for the first time, you should specify the initial layout of your dockable windows. Each time it starts after that, the layout, which may have been changed by the user during the first run, will be read from the registry or a file. So you need to set the initial layout in some way.

You can use the CExtControlBar::DockControlBarInnerOuter() and CExtControlBar::DockControlBarLTRB() methods to dock control bars to the frame window and relative to each other. This is described in this article. You can also use the CExtControlBar::SetInitDesiredSizeHorizontal and CExtControlBar::SetInitDesiredSizeVertical methods to specify their size. However, if you have a complex layout, it is not always possible to guaranty that the size of control bars will be exactly as you specified in the above two methods. The reason for this is that the size and position of a control bar can be affected by the neighboring control bars.

The alternative approach is to specify the initial layout by reading the layout information from the registry or a file even if the application starts for the first time. Here is how you can do this:

  1. Run your application. The size and position of control bars do not matter.
  2. Using the mouse, dock and resize control bars in order to get the layout you need.
  3. Exit the application. The UI state is now serialized to the registry or in a file.
  4. Include the registry information or the file in the installer of your application.
  5. When your application starts for the first time, do not dock the control bars programmatically but rather load and initialize the layout with the serialized data from the registry or the file.

How to show a hidden control bar?

You can display a control bar including when it is auto hidden, tabbed or docked in two ways:

1) Specify the control bar's dialog control identifier in WPARAM of the WM_COMMAND message and send it to the main frame window

CMainFrame * pMainFrame = . . .;
CExtControlBar * pBar = . . .;
pMainFrame->SendMessage( WM_COMMAND, WPARAM( pBar->GetDlgCtrlID() ) );

2) Use the CExtControlBar::DoFrameBarCheckCmd static method

CExtControlBar::DoFrameBarCheckCmd(
    pMainFrame, 
    nBarID,  // control bar's dialog control identifier 
    false 
    );

If you are using the auto-hide feature for control bars (i.e. you invoke the CExtControlBar::FrameInjectAutoHideAreas() method in CMainFrame::OnCreate()), do not forget to implement the following versions of MFC's OnBarCheck() and OnUpdateControlBarMenu() methods in the main frame window

BOOL CMainFrame::OnBarCheck(UINT nID)
{    
    return CExtControlBar::DoFrameBarCheckCmd( this, nID, false );
}
 
void CMainFrame::OnUpdateControlBarMenu( CCmdUI * pCmdUI )
{    
    CExtControlBar::DoFrameBarCheckUpdate( this, pCmdUI, false );
}

How to add a custom button to the caption of my control bar?

Any button in the caption of a CExtControlBar window is not a button control but rather an object of a CExtBarNcAreaButton-derived class. So such a button is windowless. There are several caption buttons provided by default (see Figure 1):

  • Hide button (the CExtBarNcAreaButtonClose class).
  • Options button (the CExtBarNcAreaButtonMenu class), which is displayed in the caption of a floating toolbar.
  • Auto Hide button (the CExtBarNcAreaButtonAutoHide class).

You can insert your custom button into the caption by overriding the CExtControlBar::OnNcAreaReinitialize() virtual method in a CExtControlBar-derived class. Just look at the code of the original method and add any caption button you need. Such a custom button should be an instance of a CExtBarNcAreaButton-derived class. You can even control its position and put it either on the right side or on the left side of the caption. The task bar (the CMyTaskAreaBar class) in the MDI sample (see Figure 2) can be used as a ready-to-use solution. The CMyTaskAreaBar::CBarNcTaskAreaBtn class implements the Back and Forward buttons. This class overrides all the needed virtual methods of the CExtBarNcAreaButton class:

  • painting in OnNcAreaDraw()
  • repositioning in OnNcAreaReposition()
  • visibility adjustment in OnNcQueryVisibility()
  • click handling in OnNcAreaClicked()
Caption buttons

Figure 1. Caption buttons

Custom caption buttons

Figure 2. Custom caption buttons: Previous page and Next page

I have an application with multiple top frame windows and each widow has its own set of control bars. All the windows share the single command profile. How to implement UI persistence for each frame?

First of all, you should allocate the command profile (using g_CmdManager->ProfileSetup()) in the InitInstance() of your CWinApp-derived class. For each of your frame classes, invoke g_CmdManager->ProfileWndAdd() in the OnCreate() handler method and g_CmdManager->ProfileWndRemove() in the DestroyWindow() virtual method. You can save and restore the UI state of control bars for each of your frame windows using the following methods:
bool CExtControlBar::ProfileBarStateLoad(
    CFrameWnd * pFrame,
    __EXT_MFC_SAFE_LPCTSTR sSectionNameCompany,
    __EXT_MFC_SAFE_LPCTSTR sSectionNameProduct,
    __EXT_MFC_SAFE_LPCTSTR sSectionNameProfile,
    LPWINDOWPLACEMENT pFrameWp = NULL,
    bool bSerializeFixedBarsState = true,
    bool bSerializeResizableBarsState = true,
    HKEY hKeyRoot = HKEY_CURRENT_USER,
    bool bEnableThrowExceptions = false
    );
bool CExtControlBar::ProfileBarStateSave(
    CFrameWnd * pFrame,
    __EXT_MFC_SAFE_LPCTSTR sSectionNameCompany,
    __EXT_MFC_SAFE_LPCTSTR sSectionNameProduct,
    __EXT_MFC_SAFE_LPCTSTR sSectionNameProfile,
    LPWINDOWPLACEMENT pFrameWp = NULL,
    bool bSerializeFixedBarsState = true,
    bool bSerializeResizableBarsState = true,
    HKEY hKeyRoot = HKEY_CURRENT_USER,
    bool bEnableThrowExceptions = false
    );
bool CExtDynamicBarSite::StateLoad(
    __EXT_MFC_SAFE_LPCTSTR sSectionNameCompany,
    __EXT_MFC_SAFE_LPCTSTR sSectionNameProduct,
    __EXT_MFC_SAFE_LPCTSTR sSectionNameProfile,
    HKEY hKeyRoot = HKEY_CURRENT_USER,
    bool bEnableThrowExceptions = false
    );
bool CExtDynamicBarSite::StateSave(
    __EXT_MFC_SAFE_LPCTSTR sSectionNameCompany,
    __EXT_MFC_SAFE_LPCTSTR sSectionNameProduct,
    __EXT_MFC_SAFE_LPCTSTR sSectionNameProfile,
    HKEY hKeyRoot = HKEY_CURRENT_USER,
    bool bEnableThrowExceptions = false
    );
Please note that you should use different registry locations for serializing your control bars. That means the sSectionNameProfile parameter should be unique for each of your frame classes. For example, _T("main frame") may be specified for the main frame window and _T("client information frame") for some other frame.

How to switch between the Visual Studio .NET and Visual Studio 2005 docking behavior of control bars?

Create a CExtPaintManager-derived class and override the CExtPaintManager::GetThemeDockingType() virtual method in it. To implement the Visual Studio .NET docking behavior, return __RESIZABLE_DOCKING_TYPE_STUDIO_2003 in this method:
virtual int GetThemeDockingType() const
{
 ASSERT_VALID( this );
 return int(CExtControlBar::__RESIZABLE_DOCKING_TYPE_STUDIO_2003);
}
If you prefer your control bars to be docked as in the Visual Studio 2005, return __RESIZABLE_DOCKING_TYPE_STUDIO_2005 instead:
virtual int GetThemeDockingType() const
{
 ASSERT_VALID( this );
 return int(CExtControlBar::__RESIZABLE_DOCKING_TYPE_STUDIO_2005);
}

I know how to add a resizable control bar to my program but I do not know how to add a dialog into it

In Prof-UIS the resizable control bar is primarily used as a container for another window. To insert a window to the control bar, just create it with the WS_CHILD window style. The following outline describes how to insert a dialog to the resizable control bar.

  1. Create a resizable control bar.
  2. Create a dialog resource and add the corresponding class using the class wizard. The dialog should have the child style (not pop-up or overlapped) and have no border. If you would like your dialog to have the anchor support and a look consistent with the themes available in Prof-UIS, use the CExtResizableDialog class rather than MFC's CDialog. In this case, we recommend you apply the Clip Children style (in the dialog resource properties) to avoid flickering when resizing the dialog.
  3. Create the dialog window inside the control bar. Your code may look like as follows:
    if( !m_wndInBarDlg.Create(
            CYourDialog::IDD,
            &m_wndResizableBar
            )
        )
    {
        TRACE0("Failed to create m_wndInBarDlg\n");
        return -1;
    }
    m_wndInBarDlg.ShowSizeGrip( FALSE );

How to use MFC's CDialogBar in Prof-UIS applications?

In Prof-UIS, there is a CExtPanelControlBar class that has similar features to MFC's CDialogBar. CExtPanelControlBar implements a toolbar-like window, which owns only one child window and has a size that is determined by the size of this child window. In most cases the panel control bar is used as a dialog container.

In the FixedSizePanels sample application you can find that the m_wndPanelDialog property of CMainFrame, which is created in CMainFrame::OnCreate() is an instance of the CExtPanelControlBar class. The m_dlgForPanelDialog property of the main frame is a child dialog whose parent window is m_wndPanelDialog.

Why when I use the CExtWRB template class for CTreeCtrl in CExtControlBar there is no scrolling in CTreeCtrl even if I specify WS_VSCROLL|WS_HSCROLL?

The SDIDOCVIEW sample application uses the CExtWRB template class for some controls used in CExtControlBar container windows. For example, the m_wndDockedCtrlTree tree control is defined in the CMainFrame class as follows:

CExtWRB < CExtWFF < CTreeCtrl > > m_wndDockedCtrlTree;

The CExtWRB template class adds a thin border around any window. This border is created as a result of recomputing and repainting the window's non-client area. But scroll bars of the tree control are not scroll bar windows, they are part of the non-client area. So, CExtWRB template class simply kills the non-client scroll bar like areas of the tree control and implements a thin border instead.

The tree controls in the ProfStudio sample are not based on the CExtWRB template class. The thin border is provided by a CProfStudioThinFrame class. You can copy this class into your application and use it. Declare all the controls without the CExtWRB class. To apply a thin frame around a window, use the following code:

CProfStudioThinFrame * pWndThinFrame = new CProfStudioThinFrame;
    if( ! pWndThinFrame->
            CreateDynamicThinFrame( &m_wndToCreateThinFrameAround )
        )
    {
        ASSERT( FALSE );
        return .......
    }

Finally we must note that CExtWRB is designed for windows without scroll bars like child dialogs.

How to disable resizing a resizable control bar?

To make a resizable control bar of a fixed size, create a CExtControlBar-derived class and override these virtual methods:
virtual void _RowResizingStart();
virtual void _RowRecalcingStart();
These methods should do nothing.

I created several controls with specifying their parent to an instance of CExtControlBar. But I can see only one control. What's wrong?

The CExtControlBar class is designed to be a container for one child window only. The resizable control bar makes this single child window fill all its client area. The CExtResizableDialog class is usually very handy for this purpose because it allows you to anchor the dialog controls so that they can be repositioned automatically. So, you may just create a dialog template resource with WS_CHILD and WS_VISIBLE window styles applied and use it as a single child window of your resizable control bar.

How to dock several control bars into one row so that they share its space in certain proportions?

To get resizable control bars docked with the frame edge so that they share the common background in certain proportions, just use the CExtControlBar::DockControlBarLTRB() method. For instance, you need to dock three control bars into one row and the first two of them should occupy 33 percent of the row length, and the third -- 34% (33+33+34=100%). Start with the third control bar and dock it by using the DockControlBarInnerOuter() method, which will make it occupy 100% of the row. Then, dock the second relative to third control bar with the DockControlBarLTRB() method and specify 66% as its share. Now the third bar occupies 34% of the row and the second -- 66%. Finally, dock the first control bar by using DockControlBarLTRB() and specify 50% of the space occupied by the second control. Here is a code snippet:

// dock Bar3 in an entirely new row at bottom
    pBar3->DockControlBarInnerOuter(
        AFX_IDW_DOCKBAR_BOTTOM,
        true,
        this,
        true
        );
    // make the second share 66% of 3d at left
    pBar2->DockControlBarLTRB(
        66,
        pBar3,
        AFX_IDW_DOCKBAR_LEFT,
        true
        ); 
    // make the first share 50% of 2d
    // at left that is equal to
    // 33% of the entire row
    pBar1->DockControlBarLTRB(
        50,
        pBar2,
        AFX_IDW_DOCKBAR_LEFT,
        true
        );
After that, three control bars are docked as it is depicted below:

How to remove the close button ("X") from the control bar?

Create a class derived from CExtControlBar (or CExtToolControlBar, or CExtMenuControlBar) and override the OnNcAreaButtonsReinitialize() virtual method.

This is the source code of the CExtControlBar::OnNcAreaButtonsReinitialize() method:

void CExtControlBar::OnNcAreaButtonsReinitialize()
{
INT nCountOfNcButtons = NcButtons_GetCount();
        if( nCountOfNcButtons > 0 )
                return;
        NcButtons_Add( new CExtBarNcAreaButtonClose(this) );
#if (!defined __EXT_MFC_NO_TAB_CONTROLBARS)
        NcButtons_Add( new CExtBarNcAreaButtonAutoHide(this) );
#endif // (!defined __EXT_MFC_NO_TAB_CONTROLBARS)
        NcButtons_Add( new CExtBarNcAreaButtonMenu(this) );
}

If the X button is unwanted, just remove this line

NcButtons_Add( new CExtBarNcAreaButtonClose(this) );

However, your control bar can still be closed when it is tab docked inside a tabbed control bar container. Such tabbed control bar container is a newly/dynamically created resizable control bar which keeps both docked CExtControlBar windows and the CExtTabWnd tab control. This tabbed container is implemented as the CExtDynTabControlBar class. To hide the Close or X button, create a CExtDynTabControlBar-derived class and override the OnNcAreaButtonsReinitialize() virtual method as it is described above for CExtControlBar.

To use your tabbed control bar container in Prof-UIS, add a message handler for the CExtControlBar::g_nMsgCreateTabbedBar registered windows message:

// declaration in the header file
afx_msg LRESULT OnMsgCreateTabbedBar( 
   WPARAM wParam, 
   LPARAM lParam 
);
 
// message map entry
ON_REGISTERED_MESSAGE(
    CExtControlBar::g_nMsgCreateTabbedBar,
    OnMsgCreateTabbedBar
    )
 
// implementation
LRESULT CMainFrame::OnMsgCreateTabbedBar(
    WPARAM wParam, LPARAM lParam )
{
CExtDynTabControlBar ** ppTabbedBar =
        (CExtDynTabControlBar **)wParam;
    ASSERT( ppTabbedBar != NULL );
    (*ppTabbedBar) =
        new C_YOUR_DynTabControlBar;
    return 0;
}

Finally, when the control bar window is in the floating state, it is inside a miniframe window which is created and destroyed automatically by Prof-UIS. The X button on this frame just hides the window.

All the non-client area buttons are instances of the classes which are derived from CExtBarNcAreaButton. You can write your own buttons and initialize them in OnNcAreaButtonsReinitialize().

Is there a way to change the appearance of the gripper for control bars?

All windows in Prof-UIS are painted with the global paint manager, which can be accessed with the g_PaintManager variable. It is a smart container for the CExtPaintManager-like object. You can create and use your own paint manager classes derived from CExtPaintManager or CExtPaintManagerXP to re-paint anything you wish.

So, create your own class derived from CExtPaintManager and override the PaintGripper() method. Please consult the code of CExtPaintManager::PaintGripper() for details. If m_bSideBar is true, the gripper of a resizable bar is painted, and this gripper is a caption. If m_bFloating is true, the paint manager paints the caption of any floating mini-frame window.

How to hide a resizable control bar?

Any resizable control bar (pBar), no matter it is docked or floating, can be hidden with the code below:
if( pBar->AutoHideModeGet() )
   pBar->AutoHideModeSet( false, false, true, true );
ASSERT_VALID( pBar->m_pDockSite );
pBar->m_pDockSite->ShowControlBar( pBar, FALSE, FALSE );

How to add an icon to the caption area of the control bar?

You can fake icons in the title bar by implementing your own non-client buttons. Each control bar can have several non-client area buttons (like "X" button when the control bar is in the floating state). The buttons can be aligned to the left or to the right. They can also paint their own areas. In the MDI sample (provided with the library) you can see the Task Panel control bar with two buttons ("<-" and "->") aligned to the right. They are clickable. What you need to do is to write your own button, which is not clickable and which will paint an icon in its area.

These buttons are derived from the CExtBarNcAreaButton class. So, you need to implement the button similar to that of "<-" (or "->") in the MDI sample, but the OnNcAreaDraw() method should paint an icon and OnNcAreaClicked() should do nothing. This button should return false in the OnQueryVisibility() method if the control bar is not in the floating state.

To insert your button into the caption of the control bar, create a class derived from CExtControlBar (or CExtToolControlBar, or CExtMenuControlBar). Override the OnNcAreaButtonsReinitialize() virtual method in your control bar:

void CMyControlBar::OnNcAreaButtonsReinitialize()
{
         CExtControlBar::OnNcAreaButtonsReinitialize()
INT nCountOfNcButtons = NcButtons_GetCount();
         if( nCountOfNcButtons > 0 )
                  return;
         NcButtons_Add( new CMyIconButton(this) );
}

As we mentioned before, CMyIconButton should be derived from CExtBarNcAreaButton. Please find the CBarNcTaskAreaBtn class in the MainFrm.h file of the MDI sample and use it as a guide of writing custom non-client buttons.

NOTE: The vertical height of a floating mini-frame window is usually less than 16 pixels. You may need to paint icons with the height a little bit less than 16. These icons may be 16x16 or 32x32, but any meaningful image should be approximately 12x12. Otherwise your icon may not fit the caption height.

How to assign a custom icon to the tab that represents an auto-hidden control bar or a control bar tabbed with other control bars?

The icon on any tab item is associated with the command corresponding to the control bar that is docked into the tab container. This icon is stored in the command manager. The command identifier is equal to the control identifier of the control bar window. Please use the following code to change the icon:
HICON hSomeNewIcon = (HICON)
	::LoadImage(
		::AfxGetResourceHandle(),
		MAKEINTRESOURCE( IDR_SOME_ICON_RESOURCE ),
		IMAGE_ICON,
		16,
		16,
		0
		);
ASSERT( hSomeNewIcon != NULL );
UINT nCmdID = (UINT) pSomeBar->GetDlgCtrlID();
VERIFY(
	g_CmdManager->CmdSetIcon(
		g_CmdManager->ProfileNameFromWnd(
			pSomeBar->GetSafeHwnd()
			),
		nCmdID,
		hSomeNewIcon,
		false
		)
	);
if( pSomeBar->IsVisible() )
	pSomeBar->GetParentFrame()->RecalcLayout();

How to handle events from caption buttons in resizable control bars?

Each button in the caption area of the resizable control bar is an instance of a CExtBarNcAreaButton-derived class. For example, the X button ("Close") is an instance of the CExtBarNcAreaButtonClose class and to handle the event that is generated by clicking on this button, you need to create a CExtBarNcAreaButtonClose-derived class and implement the OnNcAreaClicked virtual method in it. The code for the CExtBarNcAreaButtonClose::OnNcAreaClicked method (the ExtControlBar.cpp file) should give you insight into how it can be done. To initialize the caption buttons, create a class derived from CExtControlBar and override the OnNcAreaButtonsReinitialize virtual method. In this method your CExtBarNcAreaButton-derived buttons should be used instead of original ones.

If you have any questions, please visit our forum or contact our technical support team at support@prof-uis.com.