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