WTL MDI Application with Splitter
WTL MDI Application with Splitter
Introduction
This article explains how to use WTL's CSplitterWindow
class in an MDI application. The sample project included with this article is a wizard-generated MDI application enhanced with a two pane vertical splitter. On the left side of the splitter is the About dialog (thus no Help menu) and on the right side is the MDI client window.
Main Frame
Most of the relevant code for using the splitter window is contained in the mainframe.h
file of the sample project. The splitter is initialized in OnCreate()
by calling the CreateClient()
method and assigning the result to m_hWndClient
. CreateClient()
sets up the left and right panes, issues a call to CreateMDIClient()
, and assigns that result to m_hWndMDIClient
. It is important to change the parent of the MDI client to the splitter, as shown below.
// Create and populate the spltter window
HWND CreateClient()
{ // Get the Client RECT for the entire window as a starting size
RECT rcClient;
GetClientRect(&rcClient);
// Create the vertical splitter. This is the main window
m_splitter.Create(m_hWnd, rcClient, NULL, WS_CHILD | WS_VISIBLE |
WS_CLIPSIBLINGS | WS_CLIPCHILDREN);
// Create the About dialog in the left pane
m_about.Create(m_splitter.m_hWnd);
m_splitter.SetSplitterPane(SPLIT_PANE_LEFT, m_about.m_hWnd);
// Create the MDI client in the right pane
m_hWndMDIClient = CreateMDIClient();
m_splitter.SetSplitterPane(SPLIT_PANE_RIGHT, m_hWndMDIClient);
// IMPORTANT! Make the splitter the parent of the MDI client
::SetParent(m_hWndMDIClient, m_splitter.m_hWnd);
m_splitter.SetSplitterPos(132); // from left
// Splitter is ultimately the client of Main Frame (m_hWndClient)
return m_splitter.m_hWnd; }
In addition, a modification is made to the OnFileNew
handler so that the child window is assigned the proper parent: pChild->CreateEx(m_hWndMDIClient)
. It is important to use the MDI client handle when carrying out operations of this sort, since m_hWndClient
refers only to the splitter window.
Child Frame
The relevant code for handling child window activation is contained in the childfrm.h
file of the sample project. A child window with an edit control view is initialized and assigned an icon in OnCreate()
. In addition, a window message, WM_MDIACTIVATE
, is overriden to ensure proper title bar and menu activation and deactivation. Here is the handler code:
LRESULT OnMDIActivate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{ // Child to deactivate
::SendMessage((HWND)wParam, WM_NCACTIVATE, FALSE, 0);
// Child to activate
::SendMessage((HWND)lParam, WM_NCACTIVATE, TRUE, 0);
// Set focus to the MDI client
::SetFocus(m_hWndMDIClient);
// Switch to child window menu or back to default menu
if((HWND)lParam == m_hWnd && m_hMenu != NULL)
{ HMENU hWindowMenu = GetStandardWindowMenu(m_hMenu);
MDISetMenu(m_hMenu, hWindowMenu);
MDIRefreshMenu();
::DrawMenuBar(GetMainFrame()); }
else if((HWND)lParam == NULL) // last child has closed
::SendMessage(GetMainFrame(), WM_MDISETMENU, 0, 0);
bHandled = FALSE;
return 1; }
The GetMainFrame()
method called in OnMDIActivate()
is a simple wrapper function that accounts for the splitter window as follows:
HWND GetMainFrame() { return ::GetParent(GetMDIFrame()); }
Finally, another window message, WM_MENUSELECT
, is overriden to allow activation of the child window's system menu (upper left title bar icon), like this:
LRESULT OnMenuSelect(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL&)
{ return ::SendMessage(GetMainFrame(), uMsg, wParam, lParam); }
Additional Stuff
The sample project also has a few helper routines in mainframe.h
that work with the UpdateUI mechanism to enable/disable menu and toolbar buttons when a child window and its edit control are active. A WTL class, CEditCommands
is inherited by the view (mdisplitview.h
) to provide standard undo, cut, copy, and paste functions for the edit control.
The sample project also uses two WTL message map macros to exchange commands between the main frame and child frame. CHAIN_MDI_CHILD_COMMANDS()
is added to the main frame message map and CHAIN_CLIENT_COMMANDS()
is added to the child frame map so that edit commands are available from the main menu and toolbar as well as the edit control's popup menu.
Terms Of Use
The sample application available with this article is free for any purpose.
THIS SOFTWARE IS DISTRIBUTED AS-IS, WITHOUT WARRANTIES OF ANY KIND.
License
This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.
A list of licenses authors might use can be found here