Building Multiple Document Interface (MDI) Applications with WFC
June 16, 1998
Users expect applications to behave a certain way on a certain platform. Under Windows NT® and Windows® 95, one of these expected behaviors is Multiple Document Interface (MDI). For any application that opens up multiple documents, there is the expectation that these documents will be contained within the application itself. This is one of the things that makes Windows Windows, yet requires considerable effort and expertise to code using just the AWT. Windows Foundation Classes (WFC) empowers all Java programmers with MDI and gives them another compelling reason to choose Visual J++™ 6.0.
What's the Alternative?
Single Document Interface (SDI) is often used when the interface of an object or application can be expressed using a single primary window (a TextEditor) with secondary windows for supplemental tasks (a FontDialog). This reduces confusion for the user and centers the application on a single object. An example of SDI is Notepad, which can only have one document open at any given time. If you want multiple documents open and still want to use Notepad, you must open up multiple instances of Notepad itself. This can be inefficient and, for larger applications, impractical.
MDI uses a single primary window, called a parent window, to visually contain a set of related documents or child windows. Each child is essentially a primary window, but is constrained to appear only within the parent, not the entire desktop. Children typically share the menu bar, tool bar, and other parts of the parent's interface. Secondary windows like dialog boxes are generally not constrained. Good examples of MDI apps are Microsoft Word and Microsoft Excel.
Notice that MDI doesn't require your documents to be of the same type. Text, image, sound, and so forth, can all be simultaneously contained within the same parent. Also note that MDI doesn't interfere with Document/View. You are still free to present multiple views of the same data. How you control that transition (perhaps with a tool bar) is entirely up to you. MDI is simply a metaphor for managing a set of related objects and describes how these objects should behave within the greater Windows UI.
To show you how easy MDI is with WFC, we built a Generic MDI application. This application demonstrates some of the things a real MDI application must do, without getting bogged down in the document details. We define three classes: AppMain, Document, and AboutMDI.
AppMain is the entry point of our application. It provides the Container Window along with some basic MDI menu items. Notice how we call setIsMDIContainer(true) in our constructor. That's all the initialization a Form requires to become an MDI parent. In initForm() we define several menu-click events that we want to capture. File New, File Open, File Save, File Exit. These actions create new documents, open existing ones, virtually name a document, and terminate the application. We call setMDIParent() in both New and Open to notify the document that the application is its container. Documents so initialized will automatically be constrained to the application's contentView and support complex MDI behavior. Save is really a virtual save. It makes no changes to the file system, but uses a save dialog to name the document. Examine the windowCascade_click and windowTile_click calls. These methods reorganize the Child Windows to behave as expected, again with almost no coding on our part. Finally check out the initialization of menuItem10. The setMDIList(true) method attaches a submenu with all of the MDIChildren to that MenuItem. The submenu is maintained transparently by WFC. More on this later.
Document could be any Form subclass. It needs no special code to be an MDIChild and in fact our implementation contains nothing related to MDI. Actually, our Document class does very little. It places the name of the document in the title bar and reports the size of the file. You could plug almost any Form class into this example and it would work. For "real" documents you should have the document load itself and set its attributes in the constructor instead of relying on the application.
AboutMDI brings up a dialog summarizing an application's MDIChildren and Owned Forms. It demonstrates how to get the current ActiveForm, the MDIChildren array, the current Active Child, and the Owned Forms array. The Children and Owned Forms are displayed in separate list boxes with the currently active child selected. Those of you paying close attention will notice that we also tried to set our AboutMDI instance as an owned form in AppMain. Why isn't it showing up? Because we report in the AboutMDI constructor but add it as an owned form just before showDialog(). You can confirm with the debugger that our instance is indeed an owned form, but very briefly. This brings up an important point. WFC maintains both the MDIChildren array and the OwnedForms transparently. In other words, you are responsible for identifying Forms as MDIChildren and/or Owned, but WFC will remove them from these arrays if they go away or are taken over.
There are still a few things our application should do but doesn't. For one, a real application might need to keep track of the current MDIChild at all times. You could do this by overriding the onMDIChildActivate() method or preferably by assigning a delegate using addOnMDIChildActivate(). Applications that do real work also use the concept of "dirty" documents. If you've changed a document, you might want to save it before allowing it to go away. Our Document class delegates this using addOnClosing(), but the implementation doesn't do anything since these documents aren't editable.
MDI is one possible interface metaphor for managing a related set of objects. WFC handles much of the MDI behavior behind the scenes and allows you to concentrate on the problem at hand. The Form class provides two arrays for managing child forms. The MDIChildren array is for documents contained within your form. The Owned Forms array provides a consistent means of handling other top-level forms created by your application.