|
Download the DynamicBars sample application associated with this article (zipped executable files, 989 kb)
|
|
Full source code of the DynamicBars sample is available in Prof-UIS Trial and for the registered users
|
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) ()
Figure 2. Options menu of the dynamic control bar (the caption) ()
Figure 3. Options menu of the dynamic control bar (the tab) ()
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.
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).
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:
- 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.
- 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.
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
|
|