DLGCBAR32: 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(rcClientNow.left - rcClientStart.left, rcClientNow.top - rcClientStart.top); CRect rcChild; CWnd* pwndChild = GetWindow(GW_CHILD); while (pwndChild) { pwndChild->GetWindowRect(rcChild); ScreenToClient(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