matth012098
asked on
MDI switch view problem (URGENT)
Hi
I have created a MDI application in MSVC 6. I have a child window that is split into two panes. The left pane contains a CTreeView and the right pane contains a CFormView. When I click on an item within the CTreeView I would like to be able to change the CFormView in the right hand pane to another predefined CFormView containing differant controls. Any help much appreciated. Simple answer if possible (Not that used to MFC yet) with example code
Thanks
Mat
I have created a MDI application in MSVC 6. I have a child window that is split into two panes. The left pane contains a CTreeView and the right pane contains a CFormView. When I click on an item within the CTreeView I would like to be able to change the CFormView in the right hand pane to another predefined CFormView containing differant controls. Any help much appreciated. Simple answer if possible (Not that used to MFC yet) with example code
Thanks
Mat
ASKER
The code looks like it does allow switching of views although I couldn't try it out. When class wizard generates a new CFormView class from a dialog template it makes the Constructor/Destuctors protected so when I place my new CFormView derived object into the document as a member variable I get a compiler error, saying that my dervied CDocument class cannot call the constructor/destructor of my new derived CFormView object. I have assumed that I am not supposed to place new views in the CDocument class, but rather add them to the document using a document template, however if I do this I have problems with the document containing views of differant types, as in when OnFileNew() is called, it launches a dialog box to ask me which kind of file I want to create. I basically want to be able to create a CFormView derived object and then be able to switch the view in the right pane of a SplitterWnd to the newly created view, without destroying the previous one. I would greatly appreciate an short example of how to actually create a CFormView in first place. I have a new version of CSplitterWnd which allows me to change the view in a pane, but I cannot seem to be able to create the CFormView to switch into the pane.
Thanx
Mat
Thanx
Mat
ASKER
I forgot to mention that it doesn't matter whether someone replies with an answer for SDI or MDI
Mat
Mat
Hi!
You can use RUNTIME_CLASS macro and construct any views without public constructor.
for example:
{
CRuntimeClass *pClass = RUNTIME_CLASS(CMyFormView) ;
CMyFormView* pView =pClass->CreateObject();
ASSERT(pView != NULL);
// pContext can be constructed based on data of already created view
if (!pView->Create(NULL, NULL, ..., pContext))
{
ASSERT (NULL);
return FALSE;
}
// here you got window
// you can hide it for example
// or show
pView->SendMessage(WM_INIT IALUPDATE) ;
}
You can use RUNTIME_CLASS macro and construct any views without public constructor.
for example:
{
CRuntimeClass *pClass = RUNTIME_CLASS(CMyFormView)
CMyFormView* pView =pClass->CreateObject();
ASSERT(pView != NULL);
// pContext can be constructed based on data of already created view
if (!pView->Create(NULL, NULL, ..., pContext))
{
ASSERT (NULL);
return FALSE;
}
// here you got window
// you can hide it for example
// or show
pView->SendMessage(WM_INIT
}
This is really cool what you're trying to do, and I have created a small program expressly to do the same thing. If I arrived at a solution before you do, I'll be happy to pass my findings on to you, but should you get there before me, please post yours.
I'll keep you updated. This is really cool!!!
I'll keep you updated. This is really cool!!!
Create a dialog resource. Derive it from CFormView. Make sure that the following are set for the dialog, so that it can work properly as a CFormView derived class:
style:child
border:none
visible:none
Here is some sample from one of my previous answers, where one can switch between views in a SDI application:
Create the other views separately by inserting a new class. Make sure that this class is derived from the appropriate view class. Assume that this view is called
CMyNewView.
Add the following protected instance variable and member function in the CMainFrame class.
protected:
enum show {MYVIEW=1, DEFVIEW=2};
void SwitchView(show nView);
Add the following menu option : MyView (mapped to OnMyView) and
DefaultView (mapped to OnDefView)
Write the following code for OnMyView
void CMainFrame::OnMyView()
{
// TODO: Add your command handler code here
SwitchView(MYVIEW);
}
Similarly this code goes for OnDefView
void CMainFrame::OnDefView()
{
// TODO: Add your command handler code here
SwitchView(DEFVIEW);
}
Here is the code for SwitchView
void CMainFrame::SwitchView(sho w nView){
CView * poldview=GetActiveView();
CView * pnewview=(CView *)GetDlgItem(nView);
if(pnewview==NULL){
switch(nView){
case MYVIEW:
pnewview=(CView *)new CMyNewView;
break;
case DEFVIEW:
pnewview=(CView *)new CMyAppView;
break;
}
CCreateContext context;
context.m_pCurrentDoc=pold view->GetD ocument();
pnewview->Create(NULL,NULL ,0L,CFrame Wnd::rectD efault,thi s,nView,&c ontext);
pnewview->OnInitialUpdate( );
}
SetActiveView(pnewview);
pnewview->ShowWindow(SW_SH OW);
poldview->ShowWindow(SW_HI DE);
poldview->SetDlgCtrlID(pol dview->Get RuntimeCla ss()==
RUNTIME_CLASS(CMyAppView)? DEFVIEW:MY VIEW);
pnewview->SetDlgCtrlID(AFX _IDW_PANE_ FIRST);
RecalcLayout();
}
Also add these handlers:
void CMainFrame::OnUpdateViewDe fView(CCmd UI* pCmdUI)
{
// TODO: Add your command update UI handler code here
pCmdUI->Enable(!GetActiveV iew()->IsK indOf(RUNT IME_CLASS( CMyAppView )));
}
void CMainFrame::OnUpdateViewSu mmary(CCmd UI* pCmdUI)
{
// TODO: Add your command update UI handler code here
pCmdUI->Enable(!GetActiveV iew()->IsK indOf(RUNT IME_CLASS( CMyNewView )));
}
style:child
border:none
visible:none
Here is some sample from one of my previous answers, where one can switch between views in a SDI application:
Create the other views separately by inserting a new class. Make sure that this class is derived from the appropriate view class. Assume that this view is called
CMyNewView.
Add the following protected instance variable and member function in the CMainFrame class.
protected:
enum show {MYVIEW=1, DEFVIEW=2};
void SwitchView(show nView);
Add the following menu option : MyView (mapped to OnMyView) and
DefaultView (mapped to OnDefView)
Write the following code for OnMyView
void CMainFrame::OnMyView()
{
// TODO: Add your command handler code here
SwitchView(MYVIEW);
}
Similarly this code goes for OnDefView
void CMainFrame::OnDefView()
{
// TODO: Add your command handler code here
SwitchView(DEFVIEW);
}
Here is the code for SwitchView
void CMainFrame::SwitchView(sho
CView * poldview=GetActiveView();
CView * pnewview=(CView *)GetDlgItem(nView);
if(pnewview==NULL){
switch(nView){
case MYVIEW:
pnewview=(CView *)new CMyNewView;
break;
case DEFVIEW:
pnewview=(CView *)new CMyAppView;
break;
}
CCreateContext context;
context.m_pCurrentDoc=pold
pnewview->Create(NULL,NULL
pnewview->OnInitialUpdate(
}
SetActiveView(pnewview);
pnewview->ShowWindow(SW_SH
poldview->ShowWindow(SW_HI
poldview->SetDlgCtrlID(pol
RUNTIME_CLASS(CMyAppView)?
pnewview->SetDlgCtrlID(AFX
RecalcLayout();
}
Also add these handlers:
void CMainFrame::OnUpdateViewDe
{
// TODO: Add your command update UI handler code here
pCmdUI->Enable(!GetActiveV
}
void CMainFrame::OnUpdateViewSu
{
// TODO: Add your command update UI handler code here
pCmdUI->Enable(!GetActiveV
}
hm,
>> pagladasu
1. why you use ENUM instead RUNTIME_CLASS?
2. also BIG problem you create new view every time when switch - big memory requirements.
3. poldview->SetDlgCtrlID() - code wrong must be used pSplitter->IdFromColRow() function.
4. operator new require public constructor (see matth comments).
>> pagladasu
1. why you use ENUM instead RUNTIME_CLASS?
2. also BIG problem you create new view every time when switch - big memory requirements.
3. poldview->SetDlgCtrlID() - code wrong must be used pSplitter->IdFromColRow() function.
4. operator new require public constructor (see matth comments).
"pagladasu", have you tried your code to see if works? That's what I'm doing with mine!
BTW, what is the type of the argument in SwitchView?
void CMainFrame::SwitchView(sho w nView)
BTW, what is the type of the argument in SwitchView?
void CMainFrame::SwitchView(sho
"Try", I had tried this code on a SDI application and it works. The argument type of the function SwitchView is of enum type 'show', which had been declared as a protected member in the CMainFrame class. I think I mentioned it in my answer. Here it is:
Add the following protected instance variable and member function in the CMainFrame class.
protected:
enum show {MYVIEW=1, DEFVIEW=2};
void SwitchView(show nView);
Add the following protected instance variable and member function in the CMainFrame class.
protected:
enum show {MYVIEW=1, DEFVIEW=2};
void SwitchView(show nView);
"pagladasu", I shall try your code out later, and if it works, then you will have done well. If time permits, I would nevertheless like for "matth" to wait until I've concluded my test so that some kind of benchmarking can be done.
The points are not the motivating factor for me in this instance, since I can see myself having application for this procedure at sometime in the future. AAMOF, I would be willing to promote your sample as the better choice should I see no meaningful improvement from the performance mine might yield.
Speed is one thing; readability and ease of maintenance is just as important.
The points are not the motivating factor for me in this instance, since I can see myself having application for this procedure at sometime in the future. AAMOF, I would be willing to promote your sample as the better choice should I see no meaningful improvement from the performance mine might yield.
Speed is one thing; readability and ease of maintenance is just as important.
"pagladasu", I have been in this profession long enough to know that there are many ways a person can adopt to get a program working (if that's what they're primarily out to do). After spending about an hour with your sample, I realized it didn't offer much for me to continue working with it, particularly because I started making changes to it which in all fairness, after a while it began taking on a different look to the original. I didn't think I wanted to engage in this, and stopped.
Couple of the changes I experimented on yours had to do with adding new controls and (with them), new views. (I shall save my opinion for that moment when I shall be posting my sample along with an explanation of my approach.)
This much I'll say for mine, is that I began from scratch, using AppWizard to create my application, accepting the minimum help from it because I knew I would be adding more controls and views, which I wanted to incorporate as part of the program's extensibility.
Unlike yours, I used CSplitterWnd and CView in mine.
I shall be posting mine in the next 24 to 48 hours.
Couple of the changes I experimented on yours had to do with adding new controls and (with them), new views. (I shall save my opinion for that moment when I shall be posting my sample along with an explanation of my approach.)
This much I'll say for mine, is that I began from scratch, using AppWizard to create my application, accepting the minimum help from it because I knew I would be adding more controls and views, which I wanted to incorporate as part of the program's extensibility.
Unlike yours, I used CSplitterWnd and CView in mine.
I shall be posting mine in the next 24 to 48 hours.
What you can say about this discussion, Matth?
ASKER
Hi
Thanks for the help chaps. I have come up with a solution based around a piece of code I found around the net that adds the ability to switch the view of a CSplitterWnd's pane. The switch view method does however destroy the view and create a new one in the process, but I have coped with this slight problem by saving the data from the form just before it is destroyed. Here is the code for switch views in a CSplitterWnd in case it is useful to someone in the future.
CView *CSplitter::ReplaceView(in t row, int col,CRuntimeClass * pViewClass,SIZE size)
{
CCreateContext context;
BOOL bSetActive;
CView *pNewView;
if ((GetPane(row,col)->IsKind Of(pViewCl ass))==TRU E)
{
pNewView = (CView *)GetPane(row,col);
return pNewView;
}
// Get pointer to CDocument object so that it can be used in the creation
// process of the new view
CDocument * pDoc= ((CView *)GetPane(row,col))->GetDo cument();
CView * pActiveView=GetParentFrame ()->GetAct iveView();
if (pActiveView==NULL || pActiveView==GetPane(row,c ol))
bSetActive=TRUE;
else
bSetActive=FALSE;
// set flag so that document will not be deleted when view is destroyed
pDoc->m_bAutoDelete=FALSE;
// Delete existing view
((CView *) GetPane(row,col))->Destroy Window();
// set flag back to default
pDoc->m_bAutoDelete=TRUE;
// Create new view
context.m_pNewViewClass=pV iewClass;
context.m_pCurrentDoc=pDoc ;
context.m_pNewDocTemplate= NULL;
context.m_pLastView=NULL;
context.m_pCurrentFrame=NU LL;
CreateView(row,col,pViewCl ass,size, &context);
pNewView = (CView *)GetPane(row,col);
if (bSetActive==TRUE)
GetParentFrame()->SetActiv eView(pNew View);
RecalcLayout();
GetPane(row,col)->SendMess age(WM_PAI NT);
return pNewView;
}
row, col - Row and column of pane to replace
pViewClass - New view class to use (In my case I have about 8 differant types of CFormView derived types)
size - Size of the view
I call this method like so:
m_pCurrentView = ((CSplitter *)GetParent())->ReplaceVie w(0, 1, m_pViewClasses[t], size);
I call this method from within my CLeftView derived from CTreeView to switch the view in the right hand pane. I store the view so I can grab the data later on and store it somewhere, I also have to put the data back into the form whenever the view is changed.
Thanks
Mat
Thanks for the help chaps. I have come up with a solution based around a piece of code I found around the net that adds the ability to switch the view of a CSplitterWnd's pane. The switch view method does however destroy the view and create a new one in the process, but I have coped with this slight problem by saving the data from the form just before it is destroyed. Here is the code for switch views in a CSplitterWnd in case it is useful to someone in the future.
CView *CSplitter::ReplaceView(in
{
CCreateContext context;
BOOL bSetActive;
CView *pNewView;
if ((GetPane(row,col)->IsKind
{
pNewView = (CView *)GetPane(row,col);
return pNewView;
}
// Get pointer to CDocument object so that it can be used in the creation
// process of the new view
CDocument * pDoc= ((CView *)GetPane(row,col))->GetDo
CView * pActiveView=GetParentFrame
if (pActiveView==NULL || pActiveView==GetPane(row,c
bSetActive=TRUE;
else
bSetActive=FALSE;
// set flag so that document will not be deleted when view is destroyed
pDoc->m_bAutoDelete=FALSE;
// Delete existing view
((CView *) GetPane(row,col))->Destroy
// set flag back to default
pDoc->m_bAutoDelete=TRUE;
// Create new view
context.m_pNewViewClass=pV
context.m_pCurrentDoc=pDoc
context.m_pNewDocTemplate=
context.m_pLastView=NULL;
context.m_pCurrentFrame=NU
CreateView(row,col,pViewCl
pNewView = (CView *)GetPane(row,col);
if (bSetActive==TRUE)
GetParentFrame()->SetActiv
RecalcLayout();
GetPane(row,col)->SendMess
return pNewView;
}
row, col - Row and column of pane to replace
pViewClass - New view class to use (In my case I have about 8 differant types of CFormView derived types)
size - Size of the view
I call this method like so:
m_pCurrentView = ((CSplitter *)GetParent())->ReplaceVie
I call this method from within my CLeftView derived from CTreeView to switch the view in the right hand pane. I store the view so I can grab the data later on and store it somewhere, I also have to put the data back into the form whenever the view is changed.
Thanks
Mat
little error:
do not use
>>>GetPane(row,col)->SendM essage(WM_ PAINT);
GetPane(row,col)->UpdateWi ndow() will be best
also destroying and creation views is time consuming operation.
Storing pointers to the all existing views will be preffered.
for example:
CView *CSplitter::ReplaceView(in t row, int col,CRuntimeClass * pViewClass,SIZE size)
{
for (int i = 0; i < m_arptrViews.GetSize(); i++)
{
if (((CView*) m_arptrViews[i])->IsKindOf (pViewClas s))
{
ActivateView((CView*) m_arptrViews[i]);
return (CView*) m_arptrViews[i];
}
}
// new view
// Get pointer to CDocument object so that it can be used in the creation
// process of the new view
CDocument * pDoc= ((CView *)GetPane(row,col))->GetDo cument();
CView * pActiveView=GetParentFrame ()->GetAct iveView();
context.m_pNewViewClass=pV iewClass;
context.m_pCurrentDoc=pDoc ;
context.m_pNewDocTemplate= NULL;
context.m_pLastView=NULL;
context.m_pCurrentFrame=NU LL;
CMyFormView* pView =pViewClass->CreateObject( );
ASSERT(pView != NULL);
if (!pView->Create(NULL, NULL, ..., &context))
{
ASSERT (NULL);
return FALSE;
}
m_arptrViews.Add(pView);
ActivateView(pView);
}
CSplitter::ActivateView(CV iew* pView)
{
// getr existing view
CView* pViewExist = ((CView *)GetPane(row,col));
::SetWindowLong(pViewExist ->m_hWnd, GWL_ID, -1);
pViewExist->SetWindowPos(N ULL, 0,0,0,0, SWP_HIDEWINDOW|SWP_NOSIZE| SWP_NOMOVE );
// activate new
::SetWindowLong(pView->m_h Wnd, GWL_ID, IdFromColRow(row, col);
pView->SetWindowPos(NULL, 0,0,0,0, SWP_SHOWWINDOW|SWP_NOACTIV ATE|SWP_NO SIZE|SWP_N OMOVE);
// and send message to update it!
pView->SendMessage(WM_INIT IALUPDATE) ;
}
do not use
>>>GetPane(row,col)->SendM
GetPane(row,col)->UpdateWi
also destroying and creation views is time consuming operation.
Storing pointers to the all existing views will be preffered.
for example:
CView *CSplitter::ReplaceView(in
{
for (int i = 0; i < m_arptrViews.GetSize(); i++)
{
if (((CView*) m_arptrViews[i])->IsKindOf
{
ActivateView((CView*) m_arptrViews[i]);
return (CView*) m_arptrViews[i];
}
}
// new view
// Get pointer to CDocument object so that it can be used in the creation
// process of the new view
CDocument * pDoc= ((CView *)GetPane(row,col))->GetDo
CView * pActiveView=GetParentFrame
context.m_pNewViewClass=pV
context.m_pCurrentDoc=pDoc
context.m_pNewDocTemplate=
context.m_pLastView=NULL;
context.m_pCurrentFrame=NU
CMyFormView* pView =pViewClass->CreateObject(
ASSERT(pView != NULL);
if (!pView->Create(NULL, NULL, ..., &context))
{
ASSERT (NULL);
return FALSE;
}
m_arptrViews.Add(pView);
ActivateView(pView);
}
CSplitter::ActivateView(CV
{
// getr existing view
CView* pViewExist = ((CView *)GetPane(row,col));
::SetWindowLong(pViewExist
pViewExist->SetWindowPos(N
// activate new
::SetWindowLong(pView->m_h
pView->SetWindowPos(NULL, 0,0,0,0, SWP_SHOWWINDOW|SWP_NOACTIV
// and send message to update it!
pView->SendMessage(WM_INIT
}
ASKER
Hi
Sorry I forgot to unlock the question, I didn't accept pagladasu's solution since I have a lot of differant kinds of views which would mean very large code to test for them all.
Excellent solution to my problem Migel, that is round about the answer I have been looking for. Please answer this question with your comment so I can award you the points.
Thanks
Mat
Sorry I forgot to unlock the question, I didn't accept pagladasu's solution since I have a lot of differant kinds of views which would mean very large code to test for them all.
Excellent solution to my problem Migel, that is round about the answer I have been looking for. Please answer this question with your comment so I can award you the points.
Thanks
Mat
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
"migel", your sample code takes a lot for granted. It requires the reader to see your idea, but overlook the way you go about implementing it. (What good is that? How complete is it?) For example, CSplitter::ReplaceView() returns a pointer to a CView object, but that didn't stop you from returning FALSE after you tested "if (!pView->Create(NULL, NULL, ..., &context))". (And is that an ASSERT(NULL) statement you have there? What is there to ASSERT about NULL?) Following that, you did "ActivateView(pView);", and ended the function with the closing brace, but forgot there was a pointer to a CView object that had to be returned.
I am not criticizing your idea. I haven't said anything about your idea. I am talking about what I see you have written.
Your idea of storing view pointers in an array is rather appealing, though the way you choose to implement 'ActivateView' is a bit tedious. By this I mean, after both '::SetWindowLong(pViewExis t->etc)' and '::SetWindowLong(pView->et c)', how about:
-------------------------- ---------- --
pViewExist->ShowWindow(SW_ HIDE);
pView->ShowWindow(SW_SHOW) ;
AddView(pView);
RemoveView(pViewExist);
GetParentFrame()->SetActiv eView(pVie w);
GetParentFrame()->RecalcLa yout();
-------------------------- ---------- --
Then about 'ActivateView' itself, does that have a return type of ‘void’?
“matth” considering you have already decided to close this question, I’ll rest my efforts here.
I am not criticizing your idea. I haven't said anything about your idea. I am talking about what I see you have written.
Your idea of storing view pointers in an array is rather appealing, though the way you choose to implement 'ActivateView' is a bit tedious. By this I mean, after both '::SetWindowLong(pViewExis
--------------------------
pViewExist->ShowWindow(SW_
pView->ShowWindow(SW_SHOW)
AddView(pView);
RemoveView(pViewExist);
GetParentFrame()->SetActiv
GetParentFrame()->RecalcLa
--------------------------
Then about 'ActivateView' itself, does that have a return type of ‘void’?
“matth” considering you have already decided to close this question, I’ll rest my efforts here.
ASKER
Try, I am only interested in the functionality, not the actual implementation, I will be writing my own clean version.
Thanks Guys for all your help
Matth
Thanks Guys for all your help
Matth
Ideas are expressed to the computer by way of implementation, because that is how someone proves that their idea works. If the implementation sucks, how sound is the idea?
I just thought for the amount of points you were offering, you would be deserving of caviar, not some fast food hamburger.
If you feel satisfied with what you got, that is all that matters, and I'm happy for you.
I just thought for the amount of points you were offering, you would be deserving of caviar, not some fast food hamburger.
If you feel satisfied with what you got, that is all that matters, and I'm happy for you.
http://www.codeguru.com/doc_view/switching_views.shtml
thanks,
pagladasu