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.
HWND CreateClient()
{
RECT rcClient;
GetClientRect(&rcClient);
m_splitter.Create(m_hWnd, rcClient, NULL, WS_CHILD | WS_VISIBLE |
WS_CLIPSIBLINGS | WS_CLIPCHILDREN);
m_about.Create(m_splitter.m_hWnd);
m_splitter.SetSplitterPane(SPLIT_PANE_LEFT, m_about.m_hWnd);
m_hWndMDIClient = CreateMDIClient();
m_splitter.SetSplitterPane(SPLIT_PANE_RIGHT, m_hWndMDIClient);
::SetParent(m_hWndMDIClient, m_splitter.m_hWnd);
m_splitter.SetSplitterPos(132);
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)
{
::SendMessage((HWND)wParam, WM_NCACTIVATE, FALSE, 0);
::SendMessage((HWND)lParam, WM_NCACTIVATE, TRUE, 0);
::SetFocus(m_hWndMDIClient);
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)
::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.