Professional UI Solutions
Site Map   /  Register
 
 
 

Dynamic Control Bars for MDI/SDI Applications with Tabbed Interface

Download the DynamicBar sample application, 989 kb Download the DynamicBars sample application associated with this article (zipped executable files, 989 kb)
Download Prof-UIS or Prof-UIS Trial to get the complete source code of the DynamicBar sample Full source code of the DynamicBars sample is available in Prof-UIS Trial and for the registered users

Dynamic control bars for displaying control bar's content as an MDI/SDI document in MDI/SDI applications with tabbed interface

Contents

Introduction

Dynamic control bars (the CExtDynamicControlBar class) introduced in Prof-UIS 2.31 inherit functionality from ordinary resizable control bars (the CExtControlBar class) including docking against the window edges and with other control bars, tab grouping, auto hide, floating over, serialization and displaying on other monitors. The new features implemented in dynamic control bars deal with displaying their content as tabbed documents in MDI/SDI applications. This makes it possible to visualize document data and control bar content, which are completely different by their nature, in one consistent way. The classic example of implementing such a tabbed interface is Visual Studio NET/2005 (the Tabbed documents mode).

Prof-UIS provides two samples that demonstrate how to use dynamic control bars: MDI_DynamicBars and SDI_DynamicBars.

User Interface

There are three cases when it is reasonable to use dynamic control bars:

  • MDI application with MDI tabs
  • MDI application without MDI tabs
  • SDI application with SDI tab page container as the child view window with the AFX_IDW_PANE_FIRST dialog control identifier
In each of these cases, dynamic control bars support a mode called Tabbed Document (MDI Document if the application is of MDI type and there are no tabs in it at all) where the control bar content is displayed as though it is an MDI/SDI document. Each dynamic control bar features a new Options menu accessed in the following ways:
  • by clicking a down arrow button in the caption of the control bar (see Figure 1)
  • by means of the context menu on the caption of the control bar (see Figure 2)
  • by means of the context menu on the MDI/SDI tab associated with the control bar (see Figure 3)
  • in the MDI system menu (see Figure 4)

Figure 1. Options menu of the dynamic control bar (the arrow button) ()

MFC Prof-UIS Dynamic Control Bar: Options menu of the dynamic control bar (the caption)

Figure 2. Options menu of the dynamic control bar (the caption) ()

MFC Prof-UIS Dynamic Control Bar: Options menu of the dynamic control bar (the tab)

Figure 3. Options menu of the dynamic control bar (the tab) ()

MFC Prof-UIS Dynamic Control Bar: Options menu of the dynamic control bar (in the MDI system menu)

Figure 4. Options menu of the dynamic control bar (in the MDI system menu) ()

Prof-UIS automatically populates the standard list of control bars (the context menu over the dock bar area for the menu bar and toolbars) with dynamic control bars, as it is depicted on Figure 5.

MFC Prof-UIS Dynamic Control Bar: Control bar list

Figure 5. Control bar list ()

Finally, Prof-UIS provides an easy way of creating a pop-up menu with a list of dynamic control bars (see Figure 6).

MFC Prof-UIS Dynamic Control Bar: Dynamic bar list

Figure 6. Dynamic bar list ()

Under the hood

Dynamic bar site

Dynamic control bars (the CExtDynamicControlBar class) are created, fully controlled, and serialized by the dynamic bar site object (the CExtDynamicBarSite class). In most cases it is convenient to inherit the CMainFrame (which is always CMDIFrameWnd/CFrameWnd-derived) class from CExtDynamicBarSite so that you can use methods of the dynamic bar site as the methods of CMainFrame. For example:


class CMainFrame
	: public CMDIFrameWnd
	, public CExtDynamicBarSite
{
...
}

or


class CMainFrame
	: public CFrameWnd
	, public CExtDynamicBarSite
{
...
}

Before you start working with the dynamic bar site, it needs to be initialized. The appropriate place for this is the CMainFrame::OnCreate() method:


CExtDynamicBarSite::Install( this );

The dynamic bar site keeps an inner collection of dynamic control bars. This collection can be populated in two ways:

  1. The application starts for the first time and that means that the Windows registry, a file, a database or other source does not keep any information about the previous GUI state of the application and control bars in particular. In this case, each dynamic control bar should be created (allocated) with the CExtDynamicBarSite::BarAlloc() method.
  2. The CExtDynamicBarSite::StateLoad() static method successfully loads the previously saved GUI state and in this case the entire collection of dynamic control bars is populated by Prof-UIS internally. You can get an array of pointers to dynamic control bars with CExtDynamicBarSite::BarEnumGetArray().

Here is a code snippet from the CMainFrame::OnCreate() method that demonstrates how dynamic control bars can be allocated/initialized:


// allocate and initialize dynamic bars
if( ! CExtDynamicBarSite::StateLoad(
		pApp->m_pszRegistryKey,
		pApp->m_pszProfileName,
		pApp->m_pszProfileName
		)
)
{ // if all bars were not loaded from registry, then allocate them
	HICON hIcon = (HICON)
		::LoadImage(
			::AfxGetInstanceHandle(),
			MAKEINTRESOURCE(IDR_MAINFRAME),
			IMAGE_ICON,
			16,
			16,
			0
			);
	ASSERT( hIcon != NULL );
	CExtCmdIcon icon;
	icon.AssignFromHICON( hIcon, false );
	int i;
	for( i = 0; i < __DEMO_BARS_COUNT_ALL; i++ )
	{
		CString strBarCaption;
		strBarCaption.Format(
			_T("Dynamic Bar %d"),
			i
			);
		m_arrAllDynamicBars.Add(
			CExtDynamicBarSite::BarAlloc(
				strBarCaption,
				icon,
				0,
           //CSimpleControlBar is inherited from CExtDynamicControlBar
				RUNTIME_CLASS(CSimpleControlBar)
				)
			);
		ASSERT( m_arrAllDynamicBars[i] != NULL );
		m_arrAllDynamicBars[i]->m_nMdiMenuResourceID =
			IDR_DYNAMIC_BAR;
		m_arrAllDynamicBars[i]->m_nMdiAccelTableResourceID =
			IDR_MAINFRAME;
	} // for( i = 0; i < __DEMO_BARS_COUNT_ALL; i++ )
} // if all bars were not loaded from registry, then allocate them
else
{ // else just get them
	CExtDynamicBarSite::BarEnumGetArray( 
                     m_arrAllDynamicBars, 
                     false, 
                     true 
                     );
} // else just get them

Please note that each dynamic control bar has its unique command identifier. It is used when you click a menu item associated with the control bar which causes the control bar to be shown and get focused. Prof-UIS itself generates an appropriate identifier if you set the third argument of the CExtDynamicBarSite::BarAlloc() to 0. Alternatively you can set this command identifier explicitly yourself. To make the menu items correctly updated, CMainFrame::OnCmdMsg() should contain a call of a similar handler of the CExtDynamicBarSite class (otherwise all these menu items will be disabled):


BOOL CMainFrame::OnCmdMsg(
          UINT nID, 
          int nCode, 
          void* pExtra, 
          AFX_CMDHANDLERINFO* pHandlerInfo
) 
{
	if( CExtDynamicBarSite::OnCmdMsg( 
                    nID, 
                    nCode, 
                    pExtra, 
                    pHandlerInfo 
                    ) 
          ) return TRUE;
...
}

The CExtDynamicBarSite class has an m_nBarListInMenusCmdID property, which allows you to create a pop-up menu with a list of all dynamic control bars (see Figure 6). You add a pop-up menu marker to the appropriate menu resource (see Figure 7) and then just assign its ID to the m_nBarListInMenusCmdID property:


CExtDynamicBarSite::m_nBarListInMenusCmdID = ID_DYNAMIC_BARS_LIST;

Prof-UIS constructs this menu dynamically but using virtual functions of the CExtDynamicControBar class (OnInitDbsMenu() or OnInsertDynamicBarCommandIntoPopupMenu()), you can modify it.

MFC Prof-UIS Dynamic Control Bar: Reserved menu resource item

Figure 7. Pop-up menu marker

Before the application terminates, dynamic control bars should be serialized to the registry, file or some other data source and the CMainFrame::DestroyWindow() method is a good place for this:


VERIFY(
	CExtDynamicBarSite::StateSave(
		pApp->m_pszRegistryKey,
		pApp->m_pszProfileName,
		pApp->m_pszProfileName
		)
);

Two types of dynamic control bars

When you allocate a new dynamic control bar using the CExtDynamicBarSite::BarAlloc() method, you need to tell the dynamic bar site which control bar exactly you want to create (the bPersistentBar parameter). Prof-UIS provides two types of dynamic control bars: simple dynamic control bars and persistent dynamic control bars. The need for such a division only makes sense if you need to serialize your dynamic control bars more than once during the application instance's lifetime. For example, you have an application whose instance serves for many users and each user gets its own restored GUI state (the layout of control bars, their visibility, and etc.) when they log in in turn (without the necessity to restart the application). Below is described what's going on in each case.

Simple dynamic control bars (bPersistentBar==false, by default). When you call the CExtDynamicBarSite::StateLoad() method, each dynamic control bar window is destroyed and consequently all their child windows get also destroyed. And although the dynamic bar site recreates all simple bars according to serialized information, you need to recreate all child windows and assign them to the dynamic control bar windows yourself. In some cases, that may cause some unwanted flickering and redesign of the existing application.

Persistent dynamic control bars (bPersistentBar==true). When you call the CExtDynamicBarSite::StateLoad() method, dynamic control bar windows are not destroyed. Their child windows are also left untouched. The dynamic bar site just checks that the number of previously saved control bars and those in memory are the same. After that it simply renews the control bars with serialized information.

You can use control bars of both types in your application simultaneously and some methods of the dynamic bar sites allow you to specify which control bars should be affected by them, for example:


void BarEnumGetArray (
	CTypedPtrArray < 
             CPtrArray, 
             CExtDynamicControlBar * 
           > & arrAllDynamicBars,
	bool bIncludePersistentBars = true,
	bool bIncludeDynamicBars = true
);
INT BarEnumGetCount (
	bool bIncludePersistentBars = true,
	bool bIncludeDynamicBars = true
);

Custom dynamic control bars

Dynamic control bars are designed to give you the maximum control over their properties and behavior. You create a CExtDynamicControlBar-derived class and modify the default behavior by overriding its virtual methods to completely meet your requirements. For example, you can override the CExtDynamicControlBar::OnSerializeDynamicProps() virtual method to serialize some properties of the child windows of your dynamic control bars:


virtual void OnSerializeDynamicProps( CArchive & ar )
{
// Serialize standard properties first
	CExtDynamicControlBar::OnSerializeDynamicProps( ar );
// Serialize the color of the child window or whatever you want here
	if( ar.IsStoring() )
		ar << m_clrEditorBackground;
	else
		ar >> m_clrEditorBackground;
}

NOTE: Do not forget to specify a name of your own CExtDynamicControlBar class as the fourth argument of the CExtDynamicBarSite::BarAlloc() method when you allocate instances of your custom class:


CExtDynamicBarSite::BarAlloc(
	strBarCaption,
	icon,
	0,
   //Should be inherited from CExtDynamicControlBar:
	RUNTIME_CLASS(CMyDynamicControlBar)
);

Another example is the CExtDynamicControlBar::OnInitDbsMenu() method, which can be useful if you need to modify the pop-up menu with a list of dynamic control bars (see Figure 6). For instance, to add a separator, use code like this:


virtual bool OnInitDbsMenu (
	CExtPopupMenuWnd * pPopup,
	HWND hWndTrack,
	CObject * pHelperSrc,
	LPARAM lParamHelper = 0 
	)
{
CExtDynamicControlBar::OnInitDbsMenu (
	pPopup,
	hWndTrack,
	pHelperSrc,
	lParamHelper 
	);
pPopup->ItemInsert(
(UINT)CExtPopupMenuWnd::TYPE_SEPARATOR, 1);
pPopup->ItemInsert(
(UINT)CExtPopupMenuWnd::TYPE_SEPARATOR, 3);

return true;
}

Containers

If you already worked with non-dynamic control bars (the CExtControlBar class), you know that the main purpose of the control bar is to be a dockable container window for all its child windows. It is also true for dynamic control bars. Because any control bar is designed to have only one child window, you should keep that in mind when design your control bars. Typically this only child window is a dialog that contains all the controls you need including a non-dockable toolbar. You develop such a dialog class (typically, it is derived from CExtResizableDialog to provide a consistent background) and then assign its instance to your control bar when the application is initialized (e.g., in CMainFrame::OnCreate()).

FAQ about dynamic control bars

How to traverse dynamic control bars?

The typical scenario of iterating around all/relevant nodes is as follows:


//Always get the start position before traversing
POSITION pos = BarEnumGetStartPosition();
for( ; pos != NULL; )
{
	CExtDynamicControlBar * pBar = 
		BarEnumGetNext( 
			pos 
			true, // Specify persistent dynamic bars with true
			true// Specify simple dynamic bars with true
			);
	ASSERT_VALID( pBar );
	// do what you need
}

How to make dynamic control bars always visible in the pop-up menu with a list of all control bars?

By default, all the commands associated with menu items are regarded as not frequently used and to show them the user needs to click the round down arrow button. To make them always visible, get their command objects from the command manager and invoke the CExtCmdItem::StateSetBasic (bool bOn = true) method for them:


UINT nCmdID = (UINT)m_pDynamicBar->GetDlgCtrlID();
CExtCmdItem * pCmdItem = 
	g_CmdManager->CmdGetPtr(
			pApp->m_pszProfileName,
			nCmdID
			);
ASSERT( pCmdItem != NULL );
pCmdItem->StateSetBasic();

Keywords

This sample demonstrates the following keywords:

Technology, toolkit, programming language, IDE MFC, C++, Prof-UIS, Visual Studio NET, Visual Studio 6
Graphical User Interface (GUI) Tabbed interface, GUI sample, MDI applications, SDI, control bar, docking, dockable, tabbed document, tab page container, user friendly, pop-up menu, MDI windows list, tool windows, document windows, auto hide, tab groups
Prof-UIS and MFC classes CMainFrame, CMDIFrameWnd, CFrameWnd, CExtDynamicBarSite, CExtDynamicControlBar

Back To Top Other Articles...