CSplitterWnd in a Dialog based Application

Download Example Project

Had this problem before. I guess there are some more interested in a solution.

It seems CSplitterWnd is designed to be used in document/view-based applications only.
But by overriding some virtual methods in a derived class, you can make splitter windows
based on CSplitterWnd be used in dialog based application, ActiveX-Controls using MFC:

All virtual methods that call GetParentFrame() in its implementation have to be
I have done this by using existing code except
– that I replaced the call to GetParentFrame() by a call to GetParent().
– all references or pointers to CFrameWnd were changed to references or pointers to CWnd.

I derived a class CxSplitterWnd from the class CSplitterWnd and proceeded as stated above.

Then I used this class in a dialog based application in the same way as any other CWnd
derived class.
For example:

class CSampleDialog : public CDialog
	CxSplitterWnd m_wndSplitter;

BOOL CSampleDlg::OnInitDialog()
	// TODO: Add extra initialization here
	m_wndSplitter.CreateStatic(this, 1, 2);
	m_wndSplitter.CreateView(0,0,RUNTIME_CLASS(CSampleView), CSize(50,0), NULL);
	m_wndSplitter.CreateView(0,1,RUNTIME_CLASS(CSampleView), CSize(0,0), NULL);

	CRect rect = ...;

The sample attached is a dialog based application and demonstrates the use of
CxSplitterWnd. It does
nothing useful.

This is the new class declaration:

// SplitWnd.h : implementation file
class CxSplitterWnd : public CSplitterWnd
	// Construction
	CxSplitterWnd() {};
	virtual ~CxSplitterWnd() {};

	// Operations
	// Overrides
	// ClassWizard generated virtual function overrides

	// Implementation
	// These are the methods to be overridden
	virtual void StartTracking(int ht);
	virtual CWnd* GetActivePane(int* pRow = NULL, int* pCol = NULL);
	virtual void SetActivePane( int row, int col, CWnd* pWnd = NULL );
	virtual BOOL OnCommand(WPARAM wParam, LPARAM lParam);
	virtual BOOL OnNotify( WPARAM wParam, LPARAM lParam, LRESULT* pResult );
	virtual BOOL OnWndMsg( UINT message, WPARAM wParam, LPARAM lParam, LRESULT* pResult );

	// Generated message map functions
	// NOTE - the ClassWizard will add and remove member functions here.

And here the implementation file:

// SplitWnd.cpp : implementation file
#include "stdafx.h"
#include "SplitWnd.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;

// HitTest return values (values and spacing between values is important)
// Had to adopt this because it has module scope 
enum HitTestValue
	noHit = 0,
	vSplitterBox = 1,
	hSplitterBox = 2,
	bothSplitterBox = 3, // just for keyboard
	vSplitterBar1 = 101,
	vSplitterBar15 = 115,
	hSplitterBar1 = 201,
	hSplitterBar15 = 215,
	splitterIntersection1 = 301,
	splitterIntersection225 = 525

// CxSplitterWnd 

BEGIN_MESSAGE_MAP(CxSplitterWnd, CSplitterWnd)
// NOTE - the ClassWizard will add and remove mapping macros here.

CWnd* CxSplitterWnd::GetActivePane(int* pRow, int* pCol)
	CWnd* pView = GetFocus();
	// make sure the pane is a child pane of the splitter
	if (pView != NULL && !IsChildPane(pView, pRow, pCol))
	pView = NULL;
	return pView;

void CxSplitterWnd::SetActivePane( int row, int col, CWnd* pWnd)
	// set the focus to the pane
	CWnd* pPane = pWnd == NULL ? GetPane(row, col) : pWnd;

void CxSplitterWnd::StartTracking(int ht)
	if (ht == noHit)
	// GetHitRect will restrict 'm_rectLimit' as appropriate
	if (ht >= splitterIntersection1 && ht <= splitterIntersection225)
		// split two directions (two tracking rectangles)
		int row = (ht - splitterIntersection1) / 15;
		int col = (ht - splitterIntersection1) % 15;
		GetHitRect(row + vSplitterBar1, m_rectTracker);
		int yTrackOffset = m_ptTrackOffset.y;
		m_bTracking2 = TRUE;
		GetHitRect(col + hSplitterBar1, m_rectTracker2);
		m_ptTrackOffset.y = yTrackOffset;
	else if (ht == bothSplitterBox)
		// hit on splitter boxes (for keyboard)
		GetHitRect(vSplitterBox, m_rectTracker);
		int yTrackOffset = m_ptTrackOffset.y;
		m_bTracking2 = TRUE;
		GetHitRect(hSplitterBox, m_rectTracker2);
		m_ptTrackOffset.y = yTrackOffset;
		// center it
		m_rectTracker.OffsetRect(0, m_rectLimit.Height()/2);
		m_rectTracker2.OffsetRect(m_rectLimit.Width()/2, 0);
		// only hit one bar
		GetHitRect(ht, m_rectTracker);

	// steal focus and capture
	// make sure no updates are pending
	// set tracking state and appropriate cursor
	m_bTracking = TRUE;
	if (m_bTracking2)
	m_htTrack = ht;

// CSplitterWnd command routing 
BOOL CxSplitterWnd::OnCommand(WPARAM wParam, LPARAM lParam)
	if (CWnd::OnCommand(wParam, lParam))
	return TRUE;
	// route commands to the splitter to the parent frame window
	return GetParent()->SendMessage(WM_COMMAND, wParam, lParam);

BOOL CxSplitterWnd::OnNotify( WPARAM wParam, LPARAM lParam, LRESULT* pResult )
	if (CWnd::OnNotify(wParam, lParam, pResult))
	return TRUE;
	// route commands to the splitter to the parent frame window
	*pResult = GetParent()->SendMessage(WM_NOTIFY, wParam, lParam);
	return TRUE;

BOOL CxSplitterWnd::OnWndMsg(UINT message, WPARAM wParam, LPARAM lParam, LRESULT* pResult)
	// The code line below is necessary if using CxSplitterWnd in a regular dll
	// AFX_MANAGE_STATE(AfxGetStaticModuleState()); 
	return CWnd::OnWndMsg(message, wParam, lParam, pResult);

Posted: 11 May 1998

More by Author

Must Read