23.10 The multiple document interface layouts


The MDI hierarchy

Now let's say some things about taking control of the MDI. Let's review some of the material from the Document-View section of Chapter 5: Software Design Patterns.

As the user 's interface with the program, the CView has two different roles. On the one hand, it's the user's channel to the program's data. And on the other hand, the CView is a graphical Windows object that sits inside two levels of frame windows .

In terms of data, we have the hierarchy shown in Table 23.7. We include the CMainFrame here because we do sometimes keep program data in there.

Table 23.7. The application, the document, the frame, and the view in MFC.

Application class

MFC parent class

Defined in

CPopApp

CWinApp

Pop.*

CMainFrame

CMDIFrameWnd

MainFrm.*

CPopDoc

CDocument

PopDoc.*

CPopView

CView

PopView.*

And in terms of the window tree we have the hierarchy shown in Table 23.8. Note that the use of a CSplitterWnd adds an extra layer.

Table 23.8. The 'window tree' for a splitter window view in the multiple document interface.

Application class

MFC parent class

Defined in

CMainFrame

CMDIFrameWnd

MainFrm.*

CChildFrame

CMDIChildWnd

ChildFrm.*

CSplitterWnd

CSplitterWnd

ChildFrm.*

CPopView

CView

PopView.*

As we summarized it in Tables 23.2 and 23.4, there are a number of special MFC functions for getting a pointer to any one of these application classes from inside any of the other ones. Looking back at those tables you'll recall that the method for getting from the CPopApp down to the CPopViews is pretty ugly. But it's easier to move up the hierarchy of the window tree.

For reasons that we'll discuss shortly, we need a way for CMainFrame to count the number of open CChildFrame windows. So we write a CPopApp::getMDIChildCount() method that looks like this. Remember that a CMDIChild is what we call a child frame; in our program it's a CChildFrame object.

 int CMainFrame::getMDIChildCount()  {      int count = 0;      CMDIChildWnd *next, *current = MDIGetActive();          //Current focus child wnd.      if (!current)          return 0;      while (TRUE)          //We'll exit this endless loop with a return inside it.      {          count++;          MDINext();              //Move the child frame activation to the cyclically next one.          next = MDIGetActive();          if (next == current)              return count;      }  } 

Automatic tiling and maximizing

To give our Pop Framework a good behavior, we don't want to ever waste screen space by showing the user the boring gray MDI client window that shows up when no views at all are open. What we want is the following.

  • If there is only one child frame open, we want it to automatically maximize into the main frame.

  • If there is more than one child frame open, we want them to automatically tile themselves into the main frame.

  • If we resize the main frame, we want the child frames to retile so as to continue filling the frame.

You might think that a feature like this would be built into the MDI, but it isn't. This isn't necessarily a failing, this just doesn't happen to be something that the designers anticipated someone wanting to do. And, frankly, it isn't all that important a feature for the Pop Framework. It's developer gold- plating again. After all, why would we want to open more than one game document in the first place? And since we're using splitter windows anyway, we don't have much reason for opening a second view of a given doc either. Well, looking at how it works is a good way to gain a little more understanding of how MDI works. But if you're in a hurry, feel free to skip the rest of this section for now. You can always come back to it later.

What we've done here is to add a new BOOL _autotile variable to our CMainFrame class. We give it a default value of TRUE , and we put a menu item controlling it as Windows Autotile . Our CMainFrame handles this menu item's messages.

The MFC call for tiling a window 'vertically' is CMDIMainFrame::MDITile(MDITILE _VERTICAL) . We need for our CMainFrame object to call this method whenever we do any of these five things listed in the first column of Table 23.9. In the second column we list the method that we need to override to make our autotiling work.

Table 23.9. The function overrides needed for 'autotiling' a multiple document application.

Action

MFC method to override

Open a new child frame view of an open document with Window New

CMainFrame::OnWindowNew

Open a new document with File New

CPopApp::OnFileNew

Open a saved document with File Open

CPopApp::OnFileOpen

Resize the main frame window

CMainFrame::OnSize

Close a child frame window

CChildFrame::OnDestroy

To make things more complicated, if there is only one open child frame, we don't want to call MDITile , because that puts our frame inside the client area of the main frame, and we have a wasteful extra caption bar. Instead, if we only have one child frame open, we need to make sure that this one view is maximized.

In order to distinguish between the case where we have one maximized view and the case where we have a number of views that we want to tile, we need a way to count how many views are open. And this is where we have to use that CMainFrame::getMDIChildCount() method.

As an example of the changes we had to make, we print our version of CMainFrame::OnWindowNew below. (In the Pop Framework the menu selection for invoking OnWindowNew is in fact labeled Window Additional View of Current Game , rather than with the more standard label Window New .)

 void CMainFrame::OnWindowNew()  {      CMDIFrameWnd::OnWindowNew(); //Call base class handler.      //Our code. RR.          /* Keep tiling so the child frames stay "stuck" to the outer              window. The CMainFrame::getMDIChildCount() is a helper              function I added. Gets the number of existing child              frames, including this one. If there are more than one              child, then I tile, otherwise I don't tile because              whenever I have only one child, I've maximized it. You              don't want to tile when a single window is maximized as              this brings its caption bar back down into the MDI              client area. */      if (_autotile && getMDIChildCount() > 1) //          MDITile(MDITILE_VERTICAL);  } 

We make a similar change to the CMainFrame::OnSize , the CPopApp::OnFileNew() , CPopApp::OnFileOpen() , and the CChildFrame::OnDestroy() .

In the case of CChildFrame::OnDestroy() we have to work a little harder. This is the method that gets called when you kill off a child frame. Here it's possible that we had two open child frames and are now closing one of them. If only one child view is going to remain , then we need to maximize the single remaining child frame. If you're interested in how this works, you can look at the code in the Pop Framework's ChildFrame.cpp .



Software Engineering and Computer Games
Software Engineering and Computer Games
ISBN: B00406LVDU
EAN: N/A
Year: 2002
Pages: 272

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net