CSplitterWnd in a Dialog based Application

CodeGuru content and product recommendations are editorially independent. We may make money when you click on links to our partners. Learn More.

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
overridden.
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 = ...;
	m_wndSplitter.MoveWindow(&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
	public:
	CxSplitterWnd() {};
	virtual ~CxSplitterWnd() {};

	// Operations
	public:
	// Overrides
	// ClassWizard generated virtual function overrides
	//{{AFX_VIRTUAL(CxSplitterWnd)
	//}}AFX_VIRTUAL 

	// Implementation
	public:
	// 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
	protected:
	//{{AFX_MSG(CxSplitterWnd)
	// NOTE - the ClassWizard will add and remove member functions here.
	//}}AFX_MSG
	DECLARE_MESSAGE_MAP()
};

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__;
#endif

// 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)
//{{AFX_MSG_MAP(CxSplitterWnd)
// NOTE - the ClassWizard will add and remove mapping macros here.
//}}AFX_MSG_MAP
END_MESSAGE_MAP()

CWnd* CxSplitterWnd::GetActivePane(int* pRow, int* pCol)
{
	ASSERT_VALID(this);
	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;
	pPane->SetFocus();
}

void CxSplitterWnd::StartTracking(int ht)
{
ASSERT_VALID(this);
	if (ht == noHit)
		return;
	// GetHitRect will restrict 'm_rectLimit' as appropriate
	GetInsideRect(m_rectLimit);
	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);
	}
	else
	{
		// only hit one bar
		GetHitRect(ht, m_rectTracker);
	}

	// steal focus and capture
	SetCapture();
	SetFocus();
	// make sure no updates are pending
	RedrawWindow(NULL, NULL, RDW_ALLCHILDREN | RDW_UPDATENOW);
	// set tracking state and appropriate cursor
	m_bTracking = TRUE;
	OnInvertTracker(m_rectTracker);
	if (m_bTracking2)
	OnInvertTracker(m_rectTracker2);
	m_htTrack = ht;
	SetSplitCursor(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

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Must Read