Link to home
Start Free TrialLog in
Avatar of matth012098
matth012098Flag for United States of America

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
Avatar of pagladasu
pagladasu

Have a look at this site. It contains all the code that mey be needed by you;
http://www.codeguru.com/doc_view/switching_views.shtml

thanks,
pagladasu
Avatar of matth012098

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
I forgot to mention that it doesn't matter whether someone replies with an answer for SDI or MDI

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_INITIALUPDATE);
}
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!!!
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(show 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=poldview->GetDocument();
    pnewview->Create(NULL,NULL,0L,CFrameWnd::rectDefault,this,nView,&context);
      pnewview->OnInitialUpdate();
      }
      SetActiveView(pnewview);
      pnewview->ShowWindow(SW_SHOW);
      poldview->ShowWindow(SW_HIDE);
      poldview->SetDlgCtrlID(poldview->GetRuntimeClass()==
      RUNTIME_CLASS(CMyAppView)?DEFVIEW:MYVIEW);
      pnewview->SetDlgCtrlID(AFX_IDW_PANE_FIRST);
      RecalcLayout();
      }

      Also add these handlers:
      void CMainFrame::OnUpdateViewDefView(CCmdUI* pCmdUI)
      {
      // TODO: Add your command update UI handler code here
      pCmdUI->Enable(!GetActiveView()->IsKindOf(RUNTIME_CLASS(CMyAppView)));
      }

      void CMainFrame::OnUpdateViewSummary(CCmdUI* pCmdUI)
      {
      // TODO: Add your command update UI handler code here
      pCmdUI->Enable(!GetActiveView()->IsKindOf(RUNTIME_CLASS(CMyNewView)));
      }

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", 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(show nView)

"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);
"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.
"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.
What you can say about this discussion, Matth?
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(int row, int col,CRuntimeClass * pViewClass,SIZE size)
{
      CCreateContext      context;
      BOOL                  bSetActive;
      CView                  *pNewView;
            
      if ((GetPane(row,col)->IsKindOf(pViewClass))==TRUE)
      {
            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))->GetDocument();
      CView * pActiveView=GetParentFrame()->GetActiveView();
      if (pActiveView==NULL || pActiveView==GetPane(row,col))
            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))->DestroyWindow();
      // set flag back to default
      pDoc->m_bAutoDelete=TRUE;

      // Create new view                      
      context.m_pNewViewClass=pViewClass;
      context.m_pCurrentDoc=pDoc;
      context.m_pNewDocTemplate=NULL;
      context.m_pLastView=NULL;
      context.m_pCurrentFrame=NULL;

      CreateView(row,col,pViewClass,size, &context);

      pNewView = (CView *)GetPane(row,col);

      if (bSetActive==TRUE)
            GetParentFrame()->SetActiveView(pNewView);

      RecalcLayout();
      GetPane(row,col)->SendMessage(WM_PAINT);

      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())->ReplaceView(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


little error:
do not use
>>>GetPane(row,col)->SendMessage(WM_PAINT);
GetPane(row,col)->UpdateWindow() 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(int row, int col,CRuntimeClass * pViewClass,SIZE size)
{
for (int i = 0; i  < m_arptrViews.GetSize(); i++)
{
  if (((CView*) m_arptrViews[i])->IsKindOf(pViewClass))
   {
   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))->GetDocument();
CView * pActiveView=GetParentFrame()->GetActiveView();

context.m_pNewViewClass=pViewClass;
context.m_pCurrentDoc=pDoc;
context.m_pNewDocTemplate=NULL;
context.m_pLastView=NULL;
context.m_pCurrentFrame=NULL;
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(CView* pView)
{
// getr existing view
CView* pViewExist = ((CView *)GetPane(row,col));
::SetWindowLong(pViewExist->m_hWnd, GWL_ID, -1);
pViewExist->SetWindowPos(NULL, 0,0,0,0, SWP_HIDEWINDOW|SWP_NOSIZE|SWP_NOMOVE);
// activate new
::SetWindowLong(pView->m_hWnd, GWL_ID, IdFromColRow(row, col);
pView->SetWindowPos(NULL, 0,0,0,0, SWP_SHOWWINDOW|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE);
// and send message to update it!
pView->SendMessage(WM_INITIALUPDATE);
}
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

ASKER CERTIFIED SOLUTION
Avatar of migel
migel

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
"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(pViewExist->etc)' and '::SetWindowLong(pView->etc)', how about:

--------------------------------------

pViewExist->ShowWindow(SW_HIDE);
pView->ShowWindow(SW_SHOW);

AddView(pView);
RemoveView(pViewExist);

GetParentFrame()->SetActiveView(pView);
GetParentFrame()->RecalcLayout();

--------------------------------------

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.
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
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.