|

Introduction
Yes, it's yet another splitter window class. So why I release a new
class, when there are a plenty of similar classes? The answer is simple. All
splitter window classes at CodeProject can be IMHO divided into two parts:
-
These derived from original MFC
CSplitterWnd. They look nice and
give a lot of advantages to classical CSplitterWnd, but deriving
from CSplitterWnd restrict you to use only CView-derived
windows in it.
-
The others, which I can use in various windows - but when I compiled and tested
them, they didn't look as pretty as the
CSplitterWnd.
In this article I offer a class, which is not derived from CSplitterWnd.
It allows you to use simple CWnd-derived windows. But it's
combined with professional looking of the original CSplitterWnd.
Some routines I took from MFC source code. Please be forbearing while reading
this article. It's my first article at CodeProject :).
Features
My goal in CSimpleSplitterWnd design was to simulate the basic
features of CSplitterWnd. With this class you can make several
panes arranged either horizontally or vertically. It doesn't offer automatic
splitting, shared scroll bars and intersection trackers. I didn't find it very
useful - but if there are enough people, who did, I can add some of
these features :) The idea of CSimpleSplitter (compared to CSplitterWnd)
is that pane windows are independent to each other, hence they are also
responsible for their scrolling and borders.
Using the code
The layout of splitter is set in constructor:
CSimpleSplitter(int nPanes, UINT nOrientation = SSP_HORZ,
int nMinSize = 30, int nBarThickness = 3);
nPanes is number of the panes, nOrientation should be
either SSP_HORZ or SSP_VERT. nMinSize is
the minimal height (or width) of any pane - it is important in recalculating
layout algorithm, when you resize the splitter. nBarThickness is
height or width of bars between panes. All of these properties remain fixed
during lifetime of the splitter instance. The creation of splitter and its
panes is straightforward:
BOOL Create(CWnd* pParent, UINT nID = AFX_IDW_PANE_FIRST);
BOOL CreatePane(int nIndex, CWnd* pPaneWnd, DWORD dwStyle,
DWORD dwExStyle, LPCTSTR lpszClassName = NULL);
Panes are indexed from 0 to nPanes - 1. The parameters
dwStyle, dwExStyle and lpszClassName are
passed to pPaneWnd->CreateEx() (If you want to bind a created
window with a pane, use SetPane instead). You can specify for
example the borders of panes there. In demo app, the "large" panes have a WS_EX_CLIENTEDGE
extended style, while the three "flat" panes have WS_EX_STATICEDGE
extended style. The splitter bar alone is only a gray rectangle, so if you
don't specify any edge and set the gray background to pane windows, you can use
the splitter in dialogs too.
The following five methods are analogous to CSplitterWnd methods,
so they don't require a special documentation.
int GetPaneCount() const;void SetPane(int nIndex, CWnd* pPaneWnd);
CWnd* GetPane(int nIndex) const;
virtual void SetActivePane(int nIndex);
CWnd* GetActivePane(int* pIndex) const;
However, setting the pane widths or heights uses different technique:
void SetPaneSizes(const int* sizes);
You pass an array of nPanes integers. They specify relative sizes
of panes. You can use any scale, the panes are resized proportionally to the
sum of the array members. When you resize the whole splitter, the sizes of
panes are changed proportionally to their actual sizes. If necessary, they're
adjusted to m_nMinSize.
void GetPaneRect(int nIndex, CRect& rcPane) const;
void GetBarRect(int nIndex, CRect& rcBar) const;
With these functions you can retrieve the position of panes in splitter client
coordinates. Bars are indexed from 1 to nPanes - 1.
And that's all. Look at the SimpleSplitterApp demo, especially the CMainFrame::OnCreate
code, you will know everything!
Points of Interest
When I programmed the splitter, I was confused, how the framework redraws
resized window. If you resize the top or left border, the framework first only move
the window content in corresponding direction, and then it calls OnPaint().
So the window was redrawn twice and in the splitter this looked ugly.
Fortunately, there is a message handler CWnd::OnWindowPosChanging.
You can avoid the initial moving of content, if you see SWP_NOCOPYBITS
as you see in CSimpleSplitter and CChildWnd code. I
think that this is useful in many other cases that splitters.
History
- 11. 2. 2004 - First version released
- 24. 3. 2004 - Some bugs fixed (see the messages below). In the
SetPaneSizes
function now you should pass only relative sizes, not absolute.
| You must Sign In to use this message board. |
|
| | Msgs 1 to 25 of 36 (Total in Forum: 36) (Refresh) | FirstPrevNext |
|
 |
|
|
The GetBarRect function (not used in the code) appears to be incorrect. This is my fix...
void CSimpleSplitter::GetBarRect(int nIndex, CRect& rcBar) const { ASSERT(nIndex > 0 && nIndex < m_nPanes); GetClientRect(&rcBar); if (m_nOrientation == SSP_HORZ) { //rcBar.left = m_orig[nIndex]; //rcBar.right = m_orig[nIndex + 1] - m_nBarThickness; rcBar.left = m_orig [nIndex] - m_nBarThickness; rcBar.right = m_orig [nIndex]; } else { //rcBar.top = m_orig[nIndex]; //rcBar.bottom = m_orig[nIndex + 1] - m_nBarThickness; rcBar.top = m_orig [nIndex] - m_nBarThickness;; rcBar.bottom = m_orig [nIndex]; } }
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Have you ever tryed to use your Splitter Ctrl with the DOC/View Framework?! Whenn i use it, i get a av...
[code]if(!m_wndSplitter.Create(this)) { TRACE0("Splitter konnte nicht erstellt werden!"); return -1; } m_wndSplitter.SetPane(0, ((CMyView*)GetActiveView())); m_wndSplitter.SetPane(1, ((CMyView*)GetActiveView())->GetOtherWnd());[/code] I have used the debugger wich shows me, that the problem is in the MoveWindow Function cause the pParentWnd Pointer is not valid!
Any Ideas?
|
| Sign In·View Thread·PermaLink | 1.00/5 (2 votes) |
|
|
|
 |
|
|
You have done a very good implementation. Do you have solution for splitting up with keyboard entry such as DoKeyboardSplit()?
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Hi,
I 've some peculiar problem. I need to create a splitter with to separate views and all inside a CWnd derived class. So I 've to provide splitter control inside a CWnd class and not inside regular CFrameWnd derived classes. Does any body knows how to solve this? Please help me out.
Thanks in advance.
Snehasish Paul (India)
Snehasish Paul
|
| Sign In·View Thread·PermaLink | 2.00/5 (1 vote) |
|
|
|
 |
|
|
In case anybody cares, I've modified the code so that you can add panes which have an arbitrary parent (usually the parent of the splitter window). I basically modified ResizePanes so that the rect being passed to MoveWindow transforms into the client space of the pane's parent rather than the client space of the splitter. I'm sure there is another way to do this, but this works.
Here is the code. Just modify ResizePanes with this:
void CSimpleSplitter::ResizePanes() { int i; CRect rcOuter, rect;
GetClientRect(rcOuter); if (m_nOrientation == SSP_HORZ) for (i = 0; i < m_nPanes; i++) { if (m_pane[i]) { rect.left = m_orig[i]; rect.top = 0; rect.right = rect.left + m_orig[i + 1] - m_orig[i] - m_nBarThickness; rect.bottom = rect.top + rcOuter.Height();
ClientToScreen( &rect ); m_pane[i]->GetParent()->ScreenToClient( &rect ); m_pane[i]->MoveWindow( &rect ); } } else for (i = 0; i < m_nPanes; i++) { if (m_pane[i]) { rect.left = 0; rect.top = m_orig[i]; rect.right = rect.left + rcOuter.Width(); rect.bottom = rect.top + m_orig[i + 1] - m_orig[i] - m_nBarThickness;
ClientToScreen( &rect ); m_pane[i]->GetParent()->ScreenToClient( &rect ); m_pane[i]->MoveWindow( &rect ); } } }
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
I'm having trouble creating a splitter window that contains dialog items. I have a CFrameWnd-derived class that contains several splitters and dialog controls:
CEdit m_txtEntry; CEdit m_msgDisplay; CListBox m_usrList; CButton m_sndButton; CSimpleSplitter m_mainSplit, m_entrySplit, m_displaySplit; CBrush m_bkgBlank, m_bkgWhite;
CChatFrame::CChatFrame() : m_mainSplit(2, SSP_VERT, 30, 3), m_entrySplit(2, SSP_HORZ, 30, 3), m_displaySplit(2, SSP_HORZ, 30, 3), m_bkgBlank(::GetSysColor(COLOR_BTNFACE)), m_bkgWhite(RGB(255, 255, 255)) {}
HCURSOR crsArrow = ::LoadCursor(0, IDC_ARROW); CString classBlank = AfxRegisterWndClass(CS_DBLCLKS, crsArrow, (HBRUSH)m_bkgBlank, 0); CString classWhite = AfxRegisterWndClass(CS_DBLCLKS, crsArrow, (HBRUSH)m_bkgWhite, 0); UINT listFlags = WS_CHILD | WS_VISIBLE | LBS_NOSEL | LBS_HASSTRINGS | LBS_SORT | WS_VSCROLL;
const int horz_splitter_sizes[2] = {4, 1}; const int vert_splitter_sizes[2] = {2, 1};
m_mainSplit.Create(this); m_mainSplit.SetPaneSizes(vert_splitter_sizes);
m_displaySplit.Create(&m_mainSplit); m_displaySplit.SetPaneSizes(horz_splitter_sizes); m_mainSplit.SetPane(0, &m_displaySplit); m_displaySplit.CreatePane(0, &m_msgDisplay, 0, 0, classWhite); m_displaySplit.CreatePane(1, &m_usrList, listFlags, WS_EX_STATICEDGE, classBlank);
m_entrySplit.Create(&m_mainSplit); m_entrySplit.SetPaneSizes(horz_splitter_sizes); m_mainSplit.SetPane(1, &m_entrySplit); m_entrySplit.CreatePane(0, &m_txtEntry, WS_CHILD | WS_VISIBLE, WS_EX_STATICEDGE, classWhite); m_entrySplit.CreatePane(1, &m_sndButton, 0, WS_EX_STATICEDGE, classBlank);
The window is created and displayed correctly, but I am unable to manipulate the dialog items, edit the text, add strings to the list box, etc. Is there another way I should be trying to do this?
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Try to create the dialog controls in a standard way and after that put them into the splitter by CSimpleSplitter::SetPane. If you create them with CSimpleSplitter::CreatePane, the splitter will create them like normal windows by CWnd::CreateEx and I think, that you can't create dialog controls by CWnd::CreateEx.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
That's right!
Now it is very useful for me.
Example: Changing definition CWnd by CButton (for example) and "for" statement by
CRect rect; for (int i = 0; i < 3; i++) { m_wndRightTop[i].Create(_T("Caption"), WS_CHILD | WS_VISIBLE, rect, &m_wndSplitterVert, AFX_IDW_PANE_FIRST + i); m_wndSplitterVert.SetPane(i, &m_wndRightTop[i]); }
That CWnd is a really visible CWnd: Buttons can be seen and pushed
modified on Friday, April 18, 2008 11:23 AM
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Thanks for an excellent piece of code. It is simple to understand yet very useful. (Why can't MFC source code be as straightforward?)
I have a couple of suggestions:
1. It would be nice if the splitter somehow resized when the window it is embedded in resizes. But since I use it as the only window in a resizeable dialog I did this:
void CMyDialog::OnSize(UINT nType, int cx, int cy) { CBCGDialogBar::OnSize(nType, cx, cy); // call base class CWnd *pw = GetTopWindow(); // get splitter if (pw != NULL) { CRect rect; GetClientRect(rect); pw->MoveWindow(rect); // resize splitter to match parent } }
2. GetCapture() may return a temporary CWnd pointer. So:
if (GetCapture() == this) ...
may not always work. You could use a member variable as a flag or perhaps:
if (GetCapture() != NULL && GetCapture()->m_hWnd == m_hWnd) ...
3. It might be nice if the child windows resized as the splitter bar was dragged, rather than just drawing the splitter rectangle (in InvertTracker). Most computers are fast enough to redraw the windows on the fly, but you could make it an option in case someone has complicated drawing code.
4. I also have a bit of code for SetPane (for safety):
ASSERT((pPaneWnd->GetStyle() & WS_CHILD | WS_VISIBLE) == WS_CHILD | WS_VISIBLE); ASSERT(pPaneWnd->GetParent() == this); // Make sure the splitter is the parent!
Andrew Phillips aphillips @ expertcomsoft.com
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Hi, Robert, I like this splitter, but can you or anybody else help create the splitter like Newsbin Pro prog? or where can I find the window like that one? Thx, Tony 
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
 |
|
|
It's a nice class...Thank you
But I have a problem. I want all controls's parent is the main dialog
But it's seem that SetPane function assume the the pPaneWnd's parent is the Split Window. Does it ?
Thanks for your help have a nice day
Reality
Welcome
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Yes, there's a split window, which contains the pane windows. You should create the split window in CYourDialog::OnCreate and resize it to whole client area of dialog.
Robert-Antonio
"Really don't know, where is the error. I get the feeling that the computer just skips over all the comments!"
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
It is a little confusing exactly what you arre trying to do. Perhaps you need 2 (or more) dialogs in each of the splitter's panes.
(It would not make sense for the "panes" of the splitter not to be children of it.)
Andrew Phillips aphillips @ hexedit.com
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Because it does affect the window command messages. All child message send WM_COMMAND to parent in default.
Finally i let splitter forward WM_COMMAND & WM_NOTIFY messages. or You may have another better solutions ?

have a nice day
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Thank you for this great class. Is there any way to hide a pane? I tried setting size to 0 but that didn't work. I have 2 panes and I want the user to be able to toggle one on and off. Thanks.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Unfortunately no. The CSimpleSplitter design requires static count of panes. At the other side, you can set 0 width of a pane and change the pane window style to be without WS_CLIENTEDGE. I hope, this could appear, as if the pane is hidden.
Robert-Antonio
"Deliver yesterday, code today, think tomorrow!"
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
 |
|
|
Tried to modify the sample by adding dialog boxes inside the splitter, but their contents are not displayed and I only get the "move splitter" mouse cursor when moving the mouse over the pane that contains a dialog. What's wrong?
Frode
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
 |
|
|
the following code replace the m_wndLeft with a child dialog :
CDialog m_dlg; // in .h with WS_CHILD style who's id is IDD_DIALOG1
// m_wndSplitterHorz.CreatePane(0, &m_wndLeft, 0, WS_EX_CLIENTEDGE, classLeft); m_dlg.Create(IDD_DIALOG1, &m_wndSplitterHorz); m_wndSplitterHorz.SetPane(0, &m_dlg); m_dlg.ShowWindow(SW_SHOW);
But I am wondering how to embed a CFormView into the splitter.
|
| Sign In·View Thread·PermaLink | 3.00/5 (2 votes) |
|
|
|
 |
|
|
Hello,
I am using the CSizingControlBar class (http://www.codeproject.com/docking/sizecbar.asp) and I'd like to have two controls inside - separated by the splitter. I.e controlTree, then the splitter, then a propertyCtrl. How to do this? - my attemps failed.
thx for helping Dom
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
I haven't any experience with CSizingControlBar, but I noticed, that it's derived from CControlBar. So I guess, that you should create the splitter as a child of the control bar:
class CMySizingControlBar: public CSizingControlBar { ... CSimpleSplitter m_wndSplitter; };
CMySizingControlBar::OnCreate(...) { m_wndSplitter.Create(this); m_wndSplitter.CreatePane(...); ... }
|
| Sign In·View Thread·PermaLink | 5.00/5 (1 vote) |
|
|
|
 |
|
|
 |
|
|
If you mean the "Simple splitter pane" text, I've done it in OnPaint function. See CChildWnd class in the demo program.
Robert-Antonio
"It's a good luck, when you meet a real fink. Then you get a respect to normal, mid-honest people."
|
| Sign In·View Thread·PermaLink | 1.00/5 (1 vote) |
|
|
|
 |
|
|
General News Question Answer Joke Rant Admin
|