Multiple Document Interface Applications






Multiple Document Interface Applications

Menu merging is one of several important features commonly supported by MDI applications. VS05 and the .NET Framework provide a variety of tools for you to use in implementing these features.

MDI Form Configuration

The MDI style of application was invented as a way to contain a set of related windows in a single frame, as shown in Figure.

30. Sample MDI Form


An MDI form has two pieces: a parent and a child. You designate the MDI parent form by setting the IsMdiContainer property to true, and you designate the MDI child form by setting the MdiParent property before showing the form:

// MDIParentForm.Designer.cs
partial class MDIParentForm {
  ...
  void InitializeComponent() {
    ...
    this.IsMdiContainer = true;
    ...
  }
}

// MDIParentForm.cs
partial class MDIParentForm : Form {

  static int formCount = 0;

  public MDIParentForm() {
    InitializeComponent();
  }

  void newToolStripMenuItem_Click(object sender, EventArgs e) {
    // Create and show MDI child form with nice title
    MDIChildForm mdiChildForm = new MDIChildForm();
    mdiChildForm.MdiParent = this;
    mdiChildForm.Text = "MDI Child Form " + (++formCount).ToString();
    mdiChildForm.Show();
  }
}

The Form class has several MDI-related members. For example, just as the parent has a property indicating that it's an MDI parent, the child form can tell whether it's being shown as an MDI child by inspecting the IsMdiChild property. And just as a form is a collection of controls, an MDI parent form has a collection of MDI children called MdiChildren. When a child is activated, either by direct user input or by the Activate method, the MDI parent receives the MdiChildActivate event. To see or change which of the MDI children is currently active, each MDI parent form provides an ActiveMdiChild property.

Note that MDI children can appear only where the MDI parent directly exposes its form surface. For example, if you cover the entire MDI parent form with a ToolStripContainer or SplitContainer, MDI children will be hidden. Instead of using ToolStripContainer, you can use individual ToolStripPanels to host tool strip controls (see Chapter 4). For splitting, you can use the Windows Forms 1.x Splitter strip instead.

Special MDI Menus

An MDI parent is expected to have two sets of special menu items: one to arrange the children inside the parent frame, and a second one to list the active MDI children and select from them. Figure shows a typical menu.

31. The Window Menu with MDI Child Management Commands


To implement the items that arrange the children inside the parent, the Form class provides the LayoutMdi method, which takes one of the four MdiLayout enumeration values:

// MDIParentForm.cs
partial class MDIParentForm : Form {
  ...
  void arrangeIconsToolStripMenuItem_Click(
    object sender, EventArgs e) {
    this.LayoutMdi(MdiLayout.ArrangeIcons);
  }

  void cascadeToolStripMenuItem_Click(object sender, EventArgs e) {
    this.LayoutMdi(MdiLayout.Cascade);
  }

  void tileChildrenVerticallyToolStripMenuItem_Click(
    object sender, EventArgs e) {
    this.LayoutMdi(MdiLayout.TileVertical);
  }

  void tileChildrenHorizontallyToolStripMenuItem_Click(
    object sender, EventArgs e) {
    this.LayoutMdi(MdiLayout.TileHorizontal);
  }
}

One other Window menu that you may need is Close All, but it isn't supported by the LayoutMdi method. Instead, you enumerate the MDI child forms, closing them one by one:

// MDIParentForm.cs
partial class MDIParentForm : Form {
  ...
  void closeAllToolStripMenuItem_Click(object sender, EventArgs e) {
    foreach( Form mdiChildForm in MdiChildren ) {
      mdiChildForm.Close();
    }
  }
}

Implementing the active MDI child menu list is almost a code-free affair. You first specify the top-level Window menu strip item as the MenuStrip's MDIWindowListItem, as shown in Figure.

32. Declaratively Configuring the MDI Child Windows List


This action ensures that a separator is automatically shown between the last window list menu item you manually added at design-time and any dynamically added menu items for MDI children.

However, when all MDI children are closed, the separator does not disappear. To cope, you write code like this:

// MDIParentForm.cs
partial class MDIParentForm : Form {
  ...
  public MDIParentForm() {
    InitializeComponent();

    this.menuStrip.MdiWindowListItem.DropDownOpening +=
      MdiWindowListItem_DropDownOpening;
  }

  void MdiWindowListItem_DropDownOpening(object sender, EventArgs e) {
    // Hide separator if it is the last menu strip item in
    // the window list menu
    ToolStripItemCollection items =
      this.menuStrip.MdiWindowListItem.DropDownItems;
    if( items[items.Count - 1] is ToolStripSeparator ) {
      items.RemoveAt(items.Count - 1);
    }
  }
  ...
}

If you prefer to avoid spending the time it takes in VS05 to get an MDI application to this point, you can lean on VS05's MDI Parent project item template to create a reasonably comprehensive MDI parent form with all the MDI trimmings for you. [22]

[22] One way to present MDI children is with a tabbed layout, much like the one VS05 uses. For more information on how to set that up, take a look at http://www.windowsforms.net/Samples/download.aspx?PageId=1&ItemId=174&tabindex=4 (http://tinysells.com/6).

Using the MDI Parent Project Item Template

The MDI Parent template is available by right-clicking your project and choosing Add | New Item, which opens the Add New Item dialog shown in Figure.

33. Using the VS05 MDI Template


Provide the desired form name and click Add, and a new form is added to your project, like the one shown in Figure.

34. A VS05 MDI Template-Generated Form


A complete set of basic UI elements is added to the form, including a menu strip, a tool strip, and a status strip. The form's IsMdiContainer property is set to true, and the Windows menu (which should really be called Window, as per most other applications) is set as the menu strip's MdiWindowListItem. Additionally, all the items on the Windows menu are implemented.

Don't forget that even though the project item template helps a lot, it's really just a skeleton that you need to flesh out to get working the way you want. For example, the code to open a new form operates over the base Form class rather than any specific MDI child form you've created:

// MDIParent.cs
partial class MDIParent : Form {

  int childFormNumber = 0;

  public MDIParent() {
    InitializeComponent();
  }

  void ShowNewForm(object sender, EventArgs e) {
    // Create a new instance of the child form.
    Form childForm = new Form();
    // Make it a child of this MDI form before showing it.
    childForm.MdiParent = this;
    childForm.Text = "Window " + childFormNumber++;
    childForm.Show();
  }
  ...
}

But as a starting point, you may find the code produced by the MDI Parent template a great big first step, so you should take time to familiarize yourself with it. You can also familiarize yourself with the MDI application model in Chapter 14: Applications.

Menu Merging

Whether the MDI parent form is generated by a project template or built manually by you, it typically has one main menu, which is specified by setting the MainMenuStrip property that we saw earlier. Additionally, the Windows Forms Designer automatically does this on our behalf when the first menu strip is dropped onto a form:

// MDIParentForm.Designer.cs
 using System.Windows.Forms;
 ...
 partial class MDIParentForm {
   ...
   MenuStrip menuStrip;
   ...
   void InitializeComponent() {
     ...
     this.menuStrip = new MenuStrip();
     ...
     // MDIParentForm
     ...
     this.MainMenuStrip = this.menuStrip;
     ...
   }
}

This relationship is fundamental to merging, which deals with the special situations that arise when an MDI child form is maximized within an MDI parent form. For example, if an MDI child form that's devoid of controls is maximized within an MDI parent form whose MainMenuStrip property is set, the two forms become one, at least from a caption and menu strip point of view, as shown in Figure.

35. MDI Child Form Maximized in MDI Parent Form


The MDI child form's title bar text has been merged into the MDI parent form's title bar, enclosed within square brackets. Additionally, the MDI child form's system menu and maximize, minimize, and close buttons have all been merged into the MDI parent form's main menu strip. Even though the MDI parent form has no special knowledge about main menu strips, the title bar text merges without trouble. However, the MDI child form's title bar doesn't merge, as Figure demonstrates.

36. Not So Attractive Merging


To ensure an MDI child form's title bar merges, you should set the MDI parent form's MainMenuStrip property.

An MDI parent form's main menu strip typically exposes application-wide commands, but MDI child forms often have menu strips to suit their own purposes. Why not simply put everything into the parent's main menu to start with? The reason is that lots of menu items don't make sense without a childfor example, File | Closeso showing them isn't helpful. Similarly, the set of operations may vary between MDI children, so the merged menu should consist only of the items from the parent that always make sensesuch as File | Exitand the items from the currently active child.

For example, Figure shows an MDI parent form's File menu when there are no MDI child forms, and Figure shows the same File menu when there is one MDI child form.

37. MDI Parent Form's File Menu and No MDI Child Forms


38. MDI Parent Form's File Menu and One Maximized MDI Child Form


In the Windows Forms Designer, both the parent and the child forms have a main menu, as shown in Figure.

39. The MDI Parent Form and MDI Child Form Menus in the Windows Forms Designer


Notice that the MDI child form's menu items don't contain all of the items shown in Figure when the MDI child form is active at run-time. Instead, the MDI child form has only the new items that are to be added to the MDI parent form's menu. For the merging to work, two things must happen. First, we hide the MDI child form's menu strip; otherwise, it would remain visible when merging occurs. This is simply a case of setting the menu strip's Visible property to false. Second, we configure two sets of properties on the menu items to be merged: at the top level (for example, File) and at the lower levels (for example, File | New).

Merging is governed on a per-item basis via the MergeAction and MergeIndex properties that appear on each menu strip item. The MergeAction is one of the following MergeAction enumeration values:

namespace System.Windows.Forms {
  enum MergeAction {
    Append = 0, // Merge menu item at the end of the menu items
                // at the same level in the merge target (default)
    Insert = 1, // Merge menu item into the menu items at same level
                // in the merge target, at the position specified
                // by MergeIndex
    MatchOnly = 4, // If menu item is matched, merge it
                // (appending child menu items by default)
    Remove = 3, // If menu item is matched, remove matched menu
                // item from the merge target
    Replace = 2 // If menu item is matched, replace matched menu
                // item with it, consuming (not removing) child
                // menu items of the matched menu item
    }
}

Menu merging occurs in two phases: first at the top level and, if matches are found, then at the submenu level.

In the first phase, the MergeAction on each of the MDI child form's top-level menus dictates how they are merged into the MDI parent form's top-level menus. Because the default MergeAction is Append, the MDI child form's top-level menus are tacked onto the end of the MDI parent's top-level menus. When the MergeAction is set to Insert on a top-level menu item, it isn't actually merged until you also specify a MergeIndex. This is the position in the MDI parent form's top-level menu that it will be inserted into.

A MergeAction of MatchOnly means that if a top-level menu item on the MDI child form matches a top-level menu item on the MDI parent form by name, then the child menu items are merged from the MDI child form's menu into the MDI parent form's menu. In the same situation, but with a MergeAction of Remove, the MDI parent form's matched top-level menu item disappears, and the MDI child form's top-level menu item stays where it is (which should be hidden along with the menu strip).

Finally, when top-level menu items match by name and the MergeAction is Replace, the MDI parent form's top-level menu item is removed and replaced with the MDI child form's top-level menu item.

The only option that merges the menu items from both the MDI parent and the MDI child menus is Match, and, when that occurs, the second phase of menu merging occurs.

In the second phase, submenu items from the MDI child form's matching top-level menu item are merged into the matched top-level menu item on the MDI parent form. Merging of submenu items is also governed by MergeAction, which basically follows the same behavior we just described with regard to Append, MatchOnly, Remove, and Replace. However, when you specify a MergeAction of Insert, you must configure the MergeIndex on each submenu item in the matching and matched menus to ensure that they appear in the correct order.

To see how menu merging works, let's look at Figure and 2.2, which illustrate the combined MergeAction and MergeIndex settings for the menus on both MDI parent and MDI child forms.

MDI Parent Form Menu Merge Settings

MDI Parent Form Menu Item

MergeAction

MergeIndex

File

Append

-1

File | New Child

Append

-1

File | Separator

Append

-1

File | Exit

Append

-1

Window

Append

-1


MDI Child Form Menu Merge Settings

MDI Child Form Menu Item

MergeAction

MergeIndex

File

MatchOnly

-1

File | Save

Insert

1

File | Close

Insert

2

Edit

Insert

1


In this case, establishing basic menu merging involves only setting the MDI child menu strip's Visible property, and the MergeAction and MergeIndex properties of its menu strip items. The combination of MergeAction and MergeIndex supports a wide variety of merging possibilities that you can experiment with to get the desired effect. [23]

[23] New to tool strips in Windows Forms 2.0, merging is also supported on tool strips, which you configure using the same techniques you use for menu strips and menu strip items.



 Python   SQL   Java   php   Perl 
 game development   web development   internet   *nix   graphics   hardware 
 telecommunications   C++ 
 Flash   Active Directory   Windows