|
first:
add the function in ST_SplitterWnd
CWnd* ST_SplitterWnd::GetWnd(int nSide)
{
int nViewIX = m_nCurrentView[nSide] + 1;
if (nViewIX >= m_views[nSide].size())
nViewIX = 0;
return m_views[nSide][nViewIX];
}
next:
In mainfrm.cpp file...
add the code in OnCreateClient function
m_pSplitterWnd = new ST_SplitterWnd();
m_pSplitterWnd->Create(this,NULL,NULL,pContext,true);
m_nViewNo[0] = m_pSplitterWnd->AddView(LEFT_SIDE,RUNTIME_CLASS(CLeftView),pContext);
m_nViewNo[1] = m_pSplitterWnd->AddView(RIGHT_SIDE,RUNTIME_CLASS(CRightView),pContext);
m_pSplitterWndRight = m_pSplitterWnd->GetWnd(RIGHT_SIDE);
m_pSplitterWnd->SetInitialStatus();
and add the OnNotify method like below
BOOL CMainFrame::OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult)
{
// TODO: Add your specialized code here and/or call the base class
//
NMHDR *pHdr = (NMHDR*)lParam;
if(wParam == IDC_OPTIONSTREE_CTRL && pHdr->code == OT_NOTIFY_SELCHANGE)
{
return m_pSplitterWndRight->SendMessage(WM_NOTIFY, wParam, lParam);
}
return CFrameWnd::OnNotify(wParam, lParam, pResult);
}
next:
in CRightView's add the OnNotify method like below
BOOL CRightView::OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult)
{
// TODO: Add your specialized code here and/or call the base class
NMHDR *pHdr = (NMHDR*)lParam;
if(wParam == IDC_OPTIONSTREE_CTRL && pHdr->code == OT_NOTIFY_SELCHANGE)
{
CString str;
str = ((NMOPTIONTREE*)pHdr)->otiItem->GetLabelText();
}
return CView::OnNotify(wParam, lParam, pResult);
}
last:
add the code at m_otTree created.
// Want to be notified
m_otTree->SetNotify(TRUE, this->GetParent());
|
|
|
|
|
How can I set the default active pane within the MDI frame in which all the split have been done?
I've tried to catch the WM_MDIACTIVE and then something like
CView myView * mySplit->GetPane(nXdefault, nYdefault);<br />
myView->SetActiveWindow();
The handler get called, but myView doesn't become the active view among all the splits.
What's wrong?
Thanks so much,
Riccardo.
|
|
|
|
|
Hi All
there is a problem with registry entries used for the storage and retrieval of the pane sizes. Since the data structure holding the splitters is a binary tree and the Registry entries keys "sSubKey" are named using the member variable "m_nLevel" the panes which have the same level are treated to be of same size which is not neccesarrily so. Has anyone got a solution for this problem?
Thanks for neat code you have submitted by the way
ozantopoglu
|
|
|
|
|
i can not find the way to do this with this class,thank u in advance!
|
|
|
|
|
Hi,
I want to use a CFrameWnd with a DialogBar and some views for the right pane. Attached to the Dialog Bar is a CTabCtrl for switching between the views in the CFrameWnd. The problem now is, that a small part of one of the views is visible within the first view at the left upper corner.
When I use a standard CSplitterWnd I don't have this effect.
What's wrong?
Thank's
#9370
|
|
|
|
|
This code is broken. Any modifications to the example case causes problems. It certainly doesn't work with VC++ 6.
|
|
|
|
|
Then I commented out all the resize events. Even clicking the toolbar buttons tends to crash the example. Maybe the example would have been simpler without the events. Cool deal.
|
|
|
|
|
Good work !!
But I don't understand why I can't make a 3 horizontal view like this :
-----
| 1 |
-----
| 2 |
-----
| 3 |
-----
Well ... it work if, and only if each pane is created with a veiw, but if each pane is created with a NULL view in order to use AddView() like described in the article, it doesn't work any more.
There is only 2 pane !!
Is there anyone who get this ?
Is there anyone to help me please ?
Here is some code :
Splitter1 = new ST_SplitterWnd();<br />
Splitter1->Create( this, NULL, NULL, pContext, false);<br />
Splitter2 = Splitter1->AddSubDivision( BOTTOM_SIDE, NULL, NULL, pContext, false);<br />
<br />
Splitter1->AddView( TOP_SIDE, RUNTIME_CLASS(CView1), pContext);<br />
Splitter1->AddView( TOP_SIDE, RUNTIME_CLASS(CView2), pContext);<br />
Splitter1->AddView( BOTTOM_SIDE, RUNTIME_CLASS(CView1), pContext);<br />
Splitter1->AddView( BOTTOM_SIDE, RUNTIME_CLASS(CView2), pContext);<br />
Splitter2->AddView( BOTTOM_SIDE, RUNTIME_CLASS(CView1), pContext);<br />
Splitter2->AddView( BOTTOM_SIDE, RUNTIME_CLASS(CView2), pContext);
But thank you again for this class.
_____________________________
Thank you & see you soon !
MarchEmile
|
|
|
|
|
After setting up buttons to toggle my panes on/off I had to figure out how to implement it with the pane structures set up by AddSubdivision(). Here is a template of how I did it, maybe it will help someone else working along the same lines.
Pane setup: One horizontal split, bottom pane one vertical split
*********
* A *
*********
* B | C *
*********
To toggle panes on/off, using a separate handler for each, ie. using buttons or menu
#define A 0
#define BC 1
#define B 0
#define C 1
::On_A()
{
m_pSplitterWnd->ToggleSide(A);
}
::On_B()
{
m_pSplitterWnd2->ToggleSide(B);
if (m_pSplitterWnd->IsSideHidden(BC) && !m_pSplitterWnd2->IsSideHidden(B))
m_pSplitterWnd->ToggleSide(BC);
}
::On_C()
{
m_pSplitterWnd2->ToggleSide(C);
if (m_pSplitterWnd->IsSideHidden(BC) && !m_pSplitterWnd2->IsSideHidden(C))
m_pSplitterWnd->ToggleSide(BC);
}
Everything worked fine afterwords using this logic. Note that just toggling B or C was not enough, it had to be tested for turning on and if BC pane was hidden.
This logic was pretty simple for a three pane setup. More complicated setups will take more thought. I am wondering if there is a general template that could be followed for a pane setup of any complexity.
Hope this helps!
|
|
|
|
|
Keeping this contained within one thread, so replying to myself.
Question: What is worse than a splitter window?
Answer: Nested splitter windows
If anyone tried my previously posted solution for mapping events to a pane toggle in a three pane setup.....
Well, you have probably seen that there was still a problem, and also that problem has to do with the interrelationship between pane BC of the first created splitter window, and panes B and C of the sub splitter window created by AddSubDivision().
So I put on the proverbial cap and went back to take a new look and approach for a solution. What I decided to do was derive a new class ST_SplitterWndEx from ST_SplitterWnd. In my new class I added three methods, each one to toggle a distinct pane in our group of three. Now one could just as well argue that I could have foregone the new class derivation, and added three methods to my child window class, which contains the pointers to the splitter windows already. However, my methods make heavy use of the ST_SplitterWnd methods, so that is why I decided to derive from ST_SplitterWnd.
I also decided that within my methods I would only call ST_SplitterWnd public methods. That is in accord to my belief that a class is not kosher unless foreign classes can massage it and get the functionality the class was designed for.
By limiting myself to the public methods of ST_SplitterWnd, I now had the problem of how to implement pane toggling using two of it's methods, IsSideHidden() and ToggleSide(). I also accessed it's public data member m_pSubSplitterWnd.
So here is my solution for the one over two pane setup. This is a template, feel free to redefine A,B,C, and BC to whatever labels make sense in your application. First for reference, a diagram of the panes:
***********
* *
* A *
* *
***********
* * *
* B * C *
* * *
***********
In your frame window class declaration define two member pointers:
ST_SplitterWndEx* m_pSplitterWnd;
ST_SplitterWnd* m_pSplitterWnd2;
Next, in your frame windows OnCreateClient():
m_pSplitterWnd = new ST_SplitterWndEx();
m_pSplitterWnd->Create( this,
RUNTIME_CLASS(CView_A),
NULL,
pContext,
false);
m_pSplitterWnd2 = m_pSplitterWnd->AddSubDivision(BOTTOM_SIDE,
RUNTIME_CLASS(CView_B),
RUNTIME_CLASS(CView_C),
pContext,
true);
Also, be sure to destroy the ST_SplitterWndEx object in your frame class destructor, with:
delete m_pSplitterWnd;
I copied directly from my source files and changed the function names to generic labels for this demonstration.
My derived class declaration:
#if !defined(AFX_ST_SPLITTERWNDEX_H__D2C06E85_6340_44DC_8F87_293780F35CEA__INCLUDED_)
#define AFX_ST_SPLITTERWNDEX_H__D2C06E85_6340_44DC_8F87_293780F35CEA__INCLUDED_
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
#include "ST_SplitterWnd.h"
#define A 0
#define BC 1
#define B 0
#define C 1
class ST_SplitterWndEx : public ST_SplitterWnd
{
public:
BOOL Toggle_A();
BOOL Toggle_B();
BOOL Toggle_C();
ST_SplitterWndEx();
virtual ~ST_SplitterWndEx();
};
#endif // !defined(AFX_ST_SPLITTERWNDEX_H__D2C06E85_6340_44DC_8F87_293780F35CEA__INCLUDED_)
My derived class implementation:
#include "stdafx.h"
#include "ST_SplitterWndEx.h"
#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif
ST_SplitterWndEx::ST_SplitterWndEx()
{
}
ST_SplitterWndEx::~ST_SplitterWndEx()
{
}
BOOL ST_SplitterWndEx::Toggle_A()
{
BOOL bChanged;
BOOL bInitial = IsSideHidden(A);
ToggleSide(A);
bChanged = IsSideHidden(A);
if (bChanged != bInitial)
{
return TRUE;
}
else return FALSE;
}
BOOL ST_SplitterWndEx::Toggle_B()
{
BOOL bChanged;
BOOL bInitial = m_pSubSplitterWnd[BC]->IsSideHidden(B);
m_pSubSplitterWnd[BC]->ToggleSide(B);
bChanged = m_pSubSplitterWnd[BC]->IsSideHidden(B);
if (bChanged != bInitial)
{
if (!bChanged)
if (IsSideHidden(BC))
{
if (!m_pSubSplitterWnd[BC]->SideHidden(C))
m_pSubSplitterWnd[BC]->ToggleSide(C);
ToggleSide(BC);
}
return TRUE;
}
else if (m_pSubSplitterWnd[BC]->IsSideHidden(C))
{
if (IsSideHidden(A))
return FALSE;
ToggleSide(BC);
return TRUE;
}
else return FALSE;
}
BOOL ST_SplitterWndEx::Toggle_C()
{
BOOL bChanged;
BOOL bInitial = m_pSubSplitterWnd[BC]->IsSideHidden(C);
m_pSubSplitterWnd[BC]->ToggleSide(C);
bChanged = m_pSubSplitterWnd[BC]->IsSideHidden(C);
if (bChanged != bInitial)
{
if (!bChanged)
if (IsSideHidden(BC))
{
if (!m_pSubSplitterWnd[BC]->IsSideHidden(B))
m_pSubSplitterWnd[BC]->ToggleSide(B);
ToggleSide(BC);
}
return TRUE;
}
else if (m_pSubSplitterWnd[BC]->IsSideHidden(B))
{
if (IsSideHidden(A))
return FALSE;
ToggleSide(BC);
return TRUE;
}
else return FALSE;
}
By using this derived class, my application can now correctly toggle individual panes on and off, using buttons, menu selections, or whatever control I desire. Oh, and one more thing. My three methods return type BOOL, reflecting if the pane actually changed state or not. Pass this information to your CCommandUI update handler, and you can set the state of the control objects that you use to toggle the panes.
I hope this information can help anyone else that's trying to get more use out of splitter windows in their applications. Speaking from my short experience with them so far, they can be a little tricky to manipulate, but the results are worth the attempt. Happy coding!
-- modified at 2:12 Sunday 23rd October, 2005
|
|
|
|
|
Caught one little bug in ST_SplitterWnd.h
In the inline declaration
inline void SetMinSize(int i, int x) {ASSERT(i >= 0 && i <= 2); m_nPaneMinSize[i] = x;};<br />
change to
inline void SetMinSize(int i, int x) {ASSERT(i == 0 || i == 1); m_nPaneMinSize[i] = x;};<br />
m_nPaneMinSize was only allocated to hold two ints
![Smile | :)](https://www.codeproject.com/script/Forums/Images/smiley_smile.gif)
|
|
|
|
|
I have used your splitters but now I have some problems in managing events. In particular, the only class that seems to be receiving the KEYDOWN WM is the App in the preTranslateMessage. No view catches the event.
-- qweasdzxc2004
|
|
|
|
|
I am in vc++6.0,but cannot find functions such as"AddSubDivision()"and "Create()" you mationed and the definition of "ST_SplitterWnd" in MSDN.Where can I find ?
|
|
|
|
|
Hi,
like in the demo project, one of my splitter windows is associated with a custom CTreeView. Now I want my view to listen for OnContextMenu. This works so far, but I have to double-rightclick to see the context menu. I can only assume that it has something to do with the focus of the splitter window. Can anybody help me on this?
|
|
|
|
|
Found a solution for my problem. I catch WM_CONTEXTMENU on the main splitterwnd as well. Strangely the GetPane(0,0) returns the associated view you rightclicked in. Then I simple PostMessage to that view like this:
<br />
void ST_SplitterWnd::OnContextMenu(CWnd* pWnd, CPoint point)<br />
{<br />
CWnd* aWnd = GetPane(0,0);<br />
<br />
aWnd->PostMessage(WM_CONTEXTMENU, 0, (point.x)|(point.y<<16));<br />
}<br />
For this to work you need to listen for WM_CONTEXTMENU in all the views you put in the splitter windows. Otherwise the event is handed back to the parent (the splitter window) and creates a indefinte loop.
|
|
|
|
|
Could somebody explain me, what is the reason of changing the child windows ID while switching views in right pane ?? I try to analise that code, and when I comment ( not literally ) that line everything crashes, so I suppose it is extremely important. Propably I have a gap in my knowledge about how MFC works ![Frown | :(](https://www.codeproject.com/script/Forums/Images/smiley_frown.gif)
|
|
|
|
|
What I have managed to discover while debbuging, is that after CreateView returns ( I did it for 2 views, step by step just like in Authors app ), views in pane have the same ID. My highly developed skills ( :P ) tell me that there can't be more than one child window with the same ID, but what is the reason ( of course if I am wright ), and why CreateView provides still the same ID for particular pane? Please, help ![Smile | :)](https://www.codeproject.com/script/Forums/Images/smiley_smile.gif)
|
|
|
|
|
suggestion for an additional routine... the way I like it:
void ST_SplitterWnd::ToggleAuto()
{
if(IsSplittverticaly())
{
if(!IsSideHidden(LEFT_SIDE) && !IsSideHidden(RIGHT_SIDE))
{
ToggleSide(m_bAutoFirst ? RIGHT_SIDE : LEFT_SIDE);
m_bAutoFirst = !m_bAutoFirst;
}
else if(IsSideHidden(RIGHT_SIDE))
ToggleSide(RIGHT_SIDE);
else
ToggleSide(LEFT_SIDE);
}
else
{
if(!IsSideHidden(BOTTOM_SIDE) && !IsSideHidden(TOP_SIDE))
{
ToggleSide(m_bAutoFirst ? BOTTOM_SIDE : TOP_SIDE);
m_bAutoFirst = !m_bAutoFirst;
}
else if(IsSideHidden(BOTTOM_SIDE))
ToggleSide(BOTTOM_SIDE);
else
ToggleSide(TOP_SIDE);
}
}
with m_bAutoFirst as a new bool member initially set to true.
assign an accelerator key to it and you got a fast, key-controlled window splitting.
|
|
|
|
|
Could someone tell me if this is true.
The way this class splits panes, in the code example
there is a diagram (hope it show up here ok)
showing 5 panes. I labled then 1 thru 5
The first split produces two vertical panes and
from there on the Left half is further split.
As I see it Pane 2 (right pane) cannot be split once splits have started on the left pane or VV.
Is there a way to further split Pane 2 ?
+-----+--------+
| 1 | |
+--+--+ |
| 3 | | 2 |
+--+5 | |
| 4 | | |
+--+--+-------+
|
|
|
|
|
According to the demo project in this web page, please give me a hand...
Case 1 will crash, Case 2 will work but with some dummy codes
/***************************
Case 1 , ap crashed
****************************/
m_pSplitterWnd->Create(this,NULL,NULL,pContext,true);
m_pSplitterWnd->AddView(LEFT_SIDE,RUNTIME_CLASS(CSplitterWndTestView3),pContext);
m_nViewNo[0] = m_pSplitterWnd->AddView(RIGHT_SIDE,RUNTIME_CLASS(CSplitterWndTestView),pContext);
m_nViewNo[1] = m_pSplitterWnd->AddView(RIGHT_SIDE,RUNTIME_CLASS(CSplitterWndTestView6),pContext);
/***************************
Case 2 works like what i want but with dummy codes
****************************/
//When I changed the code it works but with some dummy codes
m_pSplitterWnd->Create(this,RUNTIME_CLASS(CSplitterWndTestView3),NULL,pContext,true);
//dummy 1
m_pSplitterWnd2 = m_pSplitterWnd->AddSubDivision(LEFT_SIDE,RUNTIME_CLASS(CSplitterWndTestView3),RUNTIME_CLASS(CSplitterWndTestView3),pContext,false);
//dummy 2
m_pSplitterWnd3 = m_pSplitterWnd2->AddSubDivision(BOTTOM_SIDE,NULL,RUNTIME_CLASS(CSplitterWndTestView2),pContext,true);
//dummy 3
m_pSplitterWnd4 = m_pSplitterWnd3->AddSubDivision(LEFT_SIDE,RUNTIME_CLASS(CSplitterWndTestView4),RUNTIME_CLASS(CSplitterWndTestView5),pContext,false);
// Make the right pane switchable between two different views
m_nViewNo[0] = m_pSplitterWnd->AddView(RIGHT_SIDE,RUNTIME_CLASS(CSplitterWndTestView),pContext);
m_nViewNo[1] = m_pSplitterWnd->AddView(RIGHT_SIDE,RUNTIME_CLASS(CSplitterWndTestView6),pContext);
|
|
|
|
|
Yeah, i have the same trouble maybe. The demo looks very cool, but in my own program, if i don't copy the sample code by adding four splitters and making the two AddView calls it has problems.
//This works...kind of, but i really only want two windows, one top and one //bottom
m_pSplitterWnd = new ST_SplitterWnd();
if(!m_pSplitterWnd->Create(this, NULL, NULL, pContext,false))
return FALSE;
// If i comment any of these out, even the last one, MFC asserts
//in winsplit.cpp CSplitterWnd::GetPane (GetDlgItem returns a numm pView)
m_pSplitterWnd2 = m_pSplitterWnd->AddSubDivision(LEFT_SIDE,RUNTIME_CLASS(CChildView),NULL,pContext,false);
m_pSplitterWnd3 = m_pSplitterWnd2->AddSubDivision(BOTTOM_SIDE,NULL,RUNTIME_CLASS(CChildView),pContext,true);
m_pSplitterWnd4 = m_pSplitterWnd3->AddSubDivision(LEFT_SIDE,RUNTIME_CLASS(CChildView),RUNTIME_CLASS(CChildView),pContext,false);
//if i comment these out, MFC asserts in winsplit.cpp
//CSplitterWnd::GetPane (GetDlgItem returns a numm pView)
m_nViewNo[0] = m_pSplitterWnd->AddView(RIGHT_SIDE,RUNTIME_CLASS(CChildView),pContext);
m_nViewNo[1] = m_pSplitterWnd->AddView(RIGHT_SIDE,RUNTIME_CLASS(CChildView),pContext);
m_pSplitterWnd->SetInitialStatus();
Anybody have some idea what's going one here?
Thanks.
|
|
|
|
|
Well, maybe you already solved this one, but for others that can fall into the same problem I post this.
Fist of all, the answer is present in the excellently written article... just pay attention of the NULL and RUNTIME_CLASS(something) parameters... you SHOULD use NULL for the panels you want to subdivide further and RUNTIME_CLASS(etc) on the contrary case.
Hope this help you and other people.
Greetings,
Homero De la Garza
|
|
|
|
|
// split into 4 panes :
// +-----+--------+
// | | |
// | 3 | 1 |
// |_____|________|
// | | |
// | 4 | 2 |
// +--+--+--------+
//
BOOL CMainFrame::OnCreateClient(LPCREATESTRUCT lpcs, CCreateContext* pContext)
{
m_pSplitterWnd = new ST_SplitterWnd();
m_pSplitterWnd->Create(this,NULL,NULL,pContext,true);
// split into n-e-w-s 4 parts
m_pSplitterWnd->AddSubDivision(RIGHT_SIDE,RUNTIME_CLASS(CView1),RUNTIME_CLASS(CView2),pContext,false);
m_pSplitterWnd->AddSubDivision(LEFT_SIDE,RUNTIME_CLASS(CView3),RUNTIME_CLASS(CView4),pContext,false);
m_pSplitterWnd->SetInitialStatus();
return(TRUE);
}
qt qt
|
|
|
|
|
Very nice piece of detective work.
Thanks for publishing it.
Larry
Larry Dobson
If you want to be seen, stand up.
If you want to be heard, speak up.
If you want to be respected, shut up.
William Batson
|
|
|
|
|
It is not a good practice to have users of an object 'delete' something
that they did not 'new'.
In your example project you have the deletion of the 'sub' panes in the
destructor of CMainFrame.
Objects that you created (via 'new') in ST_SplitterWnd::AddSubDivision.
A better practice is...
If you 'new' it, you 'delete' it.
Since you have a member that can be used for this purpose, you should manage
it internally.
ST_SplitterWnd::~ST_SplitterWnd()
{
SaveToRegistry();
if (m_pSubSplitterWnd[0] != NULL) delete m_pSubSplitterWnd[0];
if (m_pSubSplitterWnd[1] != NULL) delete m_pSubSplitterWnd[1];
}
The primary reason is that since most folks use the header file for an object
to determine how to use it. The header file doesn't reveal this behavior.
And, even if it did, you can manage it yourself, and should.
I am not trying to flame you . Please understand, the better code we submit,
the better the programmers will be that learn from it.
All the best to you and yours,
Larry
Larry Dobson
If you want to be seen, stand up.
If you want to be heard, speak up.
If you want to be respected, shut up.
William Batson
|
|
|
|
|