DLGCBAR: Dialog with Control Bars
 
In a Microsoft Foundation Classes application, control bars such as 
status bars and toolbars are typically attached to a frame window.  
However, for many applications a simple dialog-based user interface 
is sufficient.  MFC does not provide built-in support for adding 
control bars to dialogs.

DLGCBR is a sample application which demonstrates how to add a status 
bar and toolbar to a dialog.  In addition, it demonstrates a number of 
techniques related to using a modeless dialog as the main window of a 
MFC application.

Note that even though this is possible with MFC, if you want to write 
an app which using a single dialog window and contains a menu, toolbar, 
and status bar, you should consider using MFC's CFormView class and 
the document-view architecture, which provides more robust support 
for these features. 
                 
Adding Control Bars to a Dialog 
===============================

To add a control bar to a dialog, you must create the control bar as 
usual, then make room for the control bar within the client area of 
the dialog.  To get the control bar to function properly, the dialog 
must duplicate some of the functionality of frame windows.  If you 
want ON_UPDATE_COMMAND_UI handlers to work for the control bars, you 
also need to derive new control bar classes and handle the 
WM_IDLEUPDATECMDUI message.  If your dialog is not the main window
of your application, you will also need to modify its parent frame
window to pass the WM_IDLEUPDATECMDUI message on to the dialog's
control bars.

In the sample program, the class CModelessMain is used to implement 
the dialog's support for control bars.  The classes CDlgToolBar and 
CDlgStatusBar extended the control bar classes so ON_UPDATE_COMMAND_UI 
will work for control bars within a dialog.

To make room for a control bar within the client area of the dialog, 
follow these steps in your dialog's OnInitDialog() function:

1.  Create the control bars.  

2.  Figure out how much room the control bars will take using the 
    reposQuery option of RepositionBars():

	CRect rcClientStart;
	CRect rcClientNow;
	GetClientRect(rcClientStart);
	RepositionBars(AFX_IDW_CONTROLBAR_FIRST, 
	               AFX_IDW_CONTROLBAR_LAST,
	               0, reposQuery, rcClientNow);

3.  Move all the controls in your dialog, to account for space used 
    by control bars at the top or left of the client area.  If your 
    dialog contains a menu, you will also need to account for the 
    space used by the menu:

	CPoint ptOffset(rcClientStart.left - rcClientNow.left, 
	                rcClientStart.top - rcClientNow.top);
	ptOffset.y += ::GetSystemMetrics(SM_CYMENU);

	CRect rcChild;
	CWnd* pwndChild = GetWindow(GW_CHILD);
	while (pwndChild)
	{
	   pwndChild->GetWindowRect(rcChild);
	   rcChild.OffsetRect(ptOffset);
	   pwndChild->MoveWindow(rcChild, FALSE);
	   pwndChild = pwndChild->GetNextWindow();
	}

4.  Increase the dialog window dimensions by the amount of space 
    used by the control bars:

	CRect rcWindow;
	GetWindowRect(rcWindow);
	rcWindow.right += rcClientStart.Width() - rcClientNow.Width();
	rcWindow.bottom += rcClientStart.Height() - rcClientNow.Height();
	MoveWindow(rcWindow, FALSE);

5.  Position the control bars using RepositionBars().

To update the first pane of a status bar with menu item text, you must 
handle WM_MENUSELECT, WM_ENTERIDLE, and WM_SETMESSAGESTRING in your 
dialog class.  You need to duplicate the functionality of the CFrameWnd 
handlers for these messages.  See the CModelessMain class in the sample 
program for examples of these message handlers.

To allow ON_UPDATE_COMMAND_UI handlers to work for other status bar panes 
and for toolbar buttons, you must derive new control bar classes and 
implement a message handler for WM_IDLEUPDATECMDUI.  This is necessary 
because the default control bar implementations of OnUpdateCmdUI() assume 
the parent window is a frame window.  However, it doesn't do anything but 
pass the parent window pointer on to a function which only requires a 
CCmdTarget pointer.  So you can temporarily tell OnUpdateCmdUI() that the 
parent window pointer you are giving it is a CFrameWnd pointer, to keep 
the compiler happy.  For example,

	LRESULT CDlgToolBar::OnIdleUpdateCmdUI(WPARAM wParam, 
	                                       LPARAM lParam)
	{
	   if (IsWindowVisible())
	   {
	      CFrameWnd* pParent = (CFrameWnd*)GetParent();
	      if (pParent)
	         OnUpdateCmdUI(pParent, (BOOL)wParam);
	   }
	   return 0L;
	}
                
To pass WM_IDLEUPDATECMDUI messages on to dialogs other than the main
window, save dialog pointer(s) in your frame window class and create 
a WM_IDLEUPDATECMDUI handler in that class.  The handler should send 
the WM_IDLEUPDATECMDUI message on to the dialog(s) child windows using
CWnd::SendMessageToDescendants(), then perform default processing for 
the message within the frame window.
 
What the Sample App Does
========================
The sample application displays a list of windows.  As the user changes the 
selection in the listbox, additional information about the window is 
displayed.  Menu options are provided to refill the list immediately and to 
set an interval for automatic updates. 

The CWndListDlg class, in wndlist.h and wndlist.cpp, implements the main 
dialog window which displays the window list.  The class CRateDlg, in 
ratedlg,h and ratedlg.cpp, implements a dialog which allows the user to 
set the rate for automatic updates.

Additional Techniques
=====================
The sample program implements a number of additional features related
to using a modeless dialog as the main window of your application.  These
are described briefly below.  For further information, see the indicated
KB articles and the source listed in the cross-reference at the end of 
this file.

Modeless Dialogs
----------------
The class CModelessDialog, defined in the files modeless.h and modeless.cpp, 
is a reusable class which implements modeless dialogs, as described in KB 
article Q103788. 
    
Making a Modeless Dialog the Main Window
----------------------------------------
To use a modeless dialog as the main window, you need to allocate an instance 
of your modeless dialog class and call Create() to create the window.  You 
also need to save a pointer to the window in m_pMainWnd.  Otherwise, MFC will 
immediately exit your application.

In addition, you need to provide a way to shut your app down when the dialog 
is closed.  The class CModelessMain, defined in the files mdlsmain.h and 
mdlsmain.cpp, implements a WM_CLOSE handler which calls PostQuitMessage() to 
shut the app down.  
          
This is the minimum amount of work required to implement a modeless dialog 
as the main window.  CModelessMain adds several additional features to improve 
the appearance of the dialog.
      
Providing an Application-Specific Icon for the Dialog          
-----------------------------------------------------   
KB article Q87976 describes two methods for creating a dialog box with a custom 
icon.  CModelessMain uses the first method, which draws the icon in a WM_PAINT 
handler.  CModelessMain loads the icon in its Create() function, and assumes the 
icon has the same resource ID as the dialog it is associated with.  
         
Adding a Menu to the Dialog
---------------------------
Even though you are using a dialog as the main window of your app, you may still 
need a menu.  KB article Q84129 discusses restrictions on dialogs which contain 
menus.

This sample does not try to enforce the style limitations on dialogs, nor does 
it include support for an accelerator table.  However, it does include support 
for MFC's ON_UPDATE_COMMAND_UI handlers to maintain the state of menu items.  
(ON_COMMAND handlers work automatically.)

Menu item states are maintained by processing the WM_INITMENUPOPUP message.  The 
code for this message handler is based on the code in CFrameWnd::OnInitMenuPopup.
This implementation does not support context-sensitive help. 

Adding Status Bar Support
-------------------------
CModelessMain contains support for a status bar at the bottom of the dialog window.  
When you create a dialog, you specify the array of indicator IDs, and the number of 
indicator IDs.  CModelessMain then creates a status bar for you in OnInitDialog(),
using the technique discussed above.  

CModelessMain provides default support for ID_INDICATOR_CAPS, ID_INDICATOR_NUM, and 
ID_INDICATOR_SCRL in OnUpdateKeyIndicator().  The sample program shows how you can 
add your own status bar panes by adding a clock to the status bar.                                      

CModelessMain also supports the ID_VIEW_STATUSBAR menu option through 
OnStatusBarCheck() and OnUpdateStatusBarMenu().

Adding Toolbar Support
----------------------
CModelessMain also contains support for a toolbar at the top of the dialog window.  
When you create a dialog, you specify the array of button IDs, the number of 
button IDs, and the resource ID of the toolbar bitmap.  CModelessMain then creates 
a toolbar for you in OnInitDialog(), as discussed above.  

CModelessMain also supports the ID_VIEW_TOOLBAR menu option through OnToolBarCheck() 
and OnUpdateToolBarMenu().

Limiting the App to a Single Instance
-------------------------------------
This sample uses a technique similar to that described in KB article Q109175 to 
locate the previous instance of the application.  However, instead of searching for 
a matching class name, it searches for a matching caption.  This allows us to use 
the normal dialog class for our main window.            

                                             
Cross Reference
===============

Technique		File		Class/Function	
---------------------	--------------  -------------------------
Modeless dialog		modeless.h	CModelessDialog	
			modeless.cpp
        
Modeless dialog as	dlgcbar.cpp	CTheApp::InitInstance
   main window          mdlsmain.h	CModelessMain
			mdlsmain.cpp
													 
App-specific Icon	mdlsmain.h	CModelessMain::OnPaint
			mdlsmain.cpp	CModelessMain::OnEraseBkgnd
					CModelessMain::OnQueryDragIcon
					CModelessMain::Create													 
						                             
Menu management		mdlsmain.h	CModelessMain::OnInitMenuPopup
			mdlsmain.cpp
														
Dialog statusbar	mdlsmain.h	CModelessMain::OnInitDialog
			mdlsmain.cpp	CModelessMain::OnMenuSelect
			dlgbars.h	CModelessMain::OnEnterIdle
			dlgbars.cpp	CModelessMain::OnSetMessageString  
					CModelessMain::OnUpdateKeyIndicator
					CModelessMain::OnStatusBarCheck
					CModelessMain::OnUpdateStatusBarMenu 
					CDlgStatusBar
						               
Dialog toolbar		mdlsmain.h	CModelessMain::OnInitDialog
			mdlsmain.cpp	CModelessMain::OnToolBarCheck
			dlgbars.h	CModelessMain::OnUpdateToolBarMenu
			dlgbars.cpp	CDlgToolBar
												                            
Single-instance App	dlgcbar.h	CTheApp::InitInstance	
			dlgcbar.cpp	CTheApp::FirstInstance
											