Tải bản đầy đủ (.pdf) (50 trang)

Manning Windows Forms Programming (phần 12) pdf

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (1.95 MB, 50 trang )

516 CHAPTER 15 TREE VIEWS
As we mentioned at the start of this section, we will not spend much time discussing
these changes, since they leverage concepts and features we have seen before. Let’s
move on to editing a tree node’s label.
15.5.2 S
UPPORTING LABEL EDITS
Tree nodes can be edited in a manner similar to list items. There is a BeginEdit
method in the TreeNode class to initiate a label edit programmatically, and Befor-
eLabelEdit
and AfterLabelEdit events in the TreeView class that occur before
and after the user edits the label. Event handlers for these events receive the
NodeLa-
belEditEventArgs
class for the event parameter. This class is summarized in
.NET Table 15.5, and is manipulated in much the same way as we saw for the
LabelEditEventArgs class when handling label events for the ListView class.
5 Update the AfterSelect event
handler to use the new
DisplayPhoto method to ensure
the proper control is visible.
private void treeViewMain_AfterSelect(. . .)
{
. . .
if (node.Parent == null)
{
// Bad tag or top-level node.
LoadAlbumData(fileName);
DisplayPhoto(null);
}
else if (Path.GetExtension(fileName) . . .)
{


// Album node selected
PhotoAlbum album = OpenTreeAlbum(. . .);
LoadPhotoData(album);
DisplayPhoto(null);
}
else // must be a photograph
{
// Clear the list and display the photo
listViewMain.Clear();
DisplayPhoto(node);
}
}
6 Add a Resize event handler for
the
PictureBox control to force
the control to redraw the entire
image when it is resized.
private void pictureBoxMain_Resize
(object sender, System.EventArgs e)
{
// Force the entire control to repaint
pictureBoxMain.Invalidate();
}
DISPLAY PHOTOGRAPH IN A PICTUREBOX CONTROL (continued)

Action Result
FUN WITH TREE VIEWS 517
In our application, we will permit nodes to be edited using the
menuEditLabel
menu item, or by pressing the F2 key when a tree node is selected and the tree view

has the focus. The following table details the steps required for this change:
.NET Table 15.5 NodeLabelEditEventArgs class
The NodeLabelEditEventArgs class represents the event data associated with the Befor-
eLabelEdit and AfterLabelEdit events in the TreeView class. This class is part of the
System.Windows.Forms namespace, and inherits from the System.EventArgs class.
Public Properties
CancelEdit Gets or sets whether the edit operation should
be cancelled. This property can be set both
before and after the node is edited.
Label Gets the new text to assign to the label of the
indicated node.
Node Gets the
TreeNode object being edited.
SUPPORT EDITING OF TREE NODE LABELS
Action Result
1 Set the
LabelEdit property for
the
TreeView control to true
in the MainForm.cs [Design]
window.
Tree node labels may now be edited.
2 Handle the
KeyDown event for
the
TreeView control to initiate
a label edit when the F2 key is
pressed in the tree control.
private void treeViewMain_KeyDown
(object sender, System.Windows.

Forms.KeyEventArgs e)
{
if (e.KeyCode == Keys.F2)
{
if (treeViewMain.SelectedNode != null)
{
treeViewMain.SelectedNode.BeginEdit();
e.Handled = true;
}
}
}
3 Update the menuEdit_Popup
event handler to use the text
“Node” for the
menuEditLabel menu when
the
TreeView has the focus.
How-to
Use the Focused property for
the
TreeView class.
private void menuEdit_Popup
(object sender, System.EventArgs e)
{
if (treeViewMain.Focused)
{
menuEditLabel.Enabled
= (treeViewMain.SelectedNode != null);
menuEditLabel.Text = "&Node";
}

else // assume ListView has focus
{
menuEditLabel.Enabled
= (listViewMain.SelectedItems.Count > 0);
if (this._albumsShown)
menuEditLabel.Text = "&Name";
else
menuEditLabel.Text = "&Caption";
}
}
518 CHAPTER 15 TREE VIEWS
4 Update the menuEdit-
Label_Click event handler to
edit the appropriate item based
on the current focus.
private void menuEditLabel_Click
(object sender, System.EventArgs e)
{
if (treeViewMain.Focused)
{
if (treeViewMain.SelectedNode != null)
treeViewMain.SelectedNode.BeginEdit();
}
else if (listViewMain.SelectedItems.Count > 0)
listViewMain.SelectedItems[0].BeginEdit();
}
5 Handle the AfterLabelEdit
event for the
TreeView control.
Note: We permit the user to

edit the root node here to alter
a top-level name in the tree,
even though this change is dis-
carded when the application
exits. A more robust solution
might be to prevent this from
occurring, or to save the
change in a configuration file.
private void treeViewMain_AfterLabelEdit
(object sender, System.Windows.
Forms.NodeLabelEditEventArgs e)
{
if (e.Label == null)
{
// Edit cancelled by the user
e.CancelEdit = true;
return;
}
// No changes required for root node
if (e.Node.Parent == null)
return;
string fileName = e.Node.Tag as string;
if (Path.GetExtension(fileName) == ".abm")
e.CancelEdit = !UpdateAlbumName(e.Label,
e.Node);
else
e.CancelEdit = !UpdatePhotoCaption(e.Label,
e.Node);
}
6 Rewrite the UpdateAlbum-

Name
method to accommodate
both list items and tree nodes.
private bool UpdateAlbumName
(string newName, object obj)
{
ListViewItem item = obj as ListViewItem;
TreeNode node = obj as TreeNode;
// Determine the file name
string fileName = null;
if (item != null)
{
fileName = item.Tag as string;
node = FindNode(fileName, false);
}
else if (node != null)
fileName = node.Tag as string;
Note: Recall that the list view’s AfterLabelEdit
event handler from chapter 14 provides a
ListView-
Item
object when calling this method. This invocation
is still valid and is properly dealt with by this code.
SUPPORT EDITING OF TREE NODE LABELS (continued)
Action Result
How-to
a. Cancel the edit if the new
text is
null.
b. Do nothing if the node is a

root node.
c. For an album node, use the
UpdateAlbumName.method.
d. For a photograph node, use
the
UpdatePhotoCaption
method.
How-to
a. Change the second parame-
ter to an
object rather than
a
ListViewItem.
b. Convert the given
object to
both a list item and a tree
node.
c. Determine the file name for
the appropriate object.
d. If the object is a list view
item, also find the node cor-
responding to this item.
FUN WITH TREE VIEWS 519
7 Rename the file.
How-to
Use the RenameFile method
from chapter 14.
// Rename the file
string newFileName = null;
if (fileName != null)

{
newFileName
= RenameFile(fileName, newName, ".abm");
}
if (newFileName == null)
{
MessageBox.Show("Unable to rename album "
+ "to this name.");
return false;
}
8 Update the Tag property for the
appropriate object.
Note: When the object is a list
item, this updates the corre-
sponding node as well.
// Update the appropriate Tag property
if (item != null)
{
item.Tag = newFileName;
if (node != null)
node.Text = newName;
}
else if (node != null)
node.Tag = newFileName;
return true;
}
9 Rewrite the UpdatePhoto-
Caption method to accom-
modate both list items and tree
nodes.

private bool UpdatePhotoCaption
(string caption, object obj)
{
ListViewItem item = obj as ListViewItem;
TreeNode node = obj as TreeNode;
// Determine the album index
int index = -1;
if ((item != null) && (item.Tag is int))
{
index = (int)item.Tag;
node = FindNode(_album[index].FileName,
false);
}
else if (node != null)
{
index = node.Index;
}
10 Return false if the caption
cannot be updated.
if ((caption.Length == 0) || (index < 0))
{
MessageBox.Show("Invalid caption value.");
return false;
}
SUPPORT EDITING OF TREE NODE LABELS (continued)
Action Result
How-to
a. Change the second param-
eter to an
object rather

than a
ListViewItem.
b. Convert the given
object
to both a list item and a
tree node.
c. Determine the album index
for the appropriate object.
d. If the object is a list view
item, also find the node
corresponding to this item.
520 CHAPTER 15 TREE VIEWS
Our program now permits editing of nodes in the TreeView and items in the List-
View
. Editing is initiated with the menuLabelEdit menu or the F2 key, and is
based on which control currently has the focus.
In both update methods, note how the
as keyword is used to convert the given
object into both a
TreeView and a ListView, as is shown in the following excerpt.
The remainder of each method executes the appropriate statements based on which
type of control is provided.
ListViewItem item = obj as ListViewItem;
TreeNode node = obj as TreeNode;
Also of note is our use of the FindNode method created earlier in the chapter as part
of section 15.4.2. As you may recall, we included a parameter to this method that
indicated whether to expand the selected node. We set this second parameter to
false here to ensure that the contents of the tree view control are not altered.
Our final change is to support the display of our album and photograph property
dialogs from the

TreeView control.
15.5.3 U
PDATING THE PROPERTIES MENU
In chapter 14 we created a Properties menu. We handled the Click event for this
menu in a
menuProperties_Click method, and created the DisplayAlbumProp-
erties
and DisplayPhotoProperties methods to display the two types of dialogs
required. Here we would like to change the behavior of this menu to the following:
• When the
TreeView has the focus, display the appropriate properties dialog if
an album node or a photograph node is selected.
• When the
ListView has the focus, display the appropriate properties dialog
for the selected item.
• When the
PictureBox has the focus, display the photograph properties dia-
log associated with the displayed image.
To make this change, we will modify our Display methods to accept either a
List-
ViewItem
or a TreeNode object. The following table details the changes required.
11 Update the photograph’s
caption, and save the changes
to the album.
Note: When the object is a list
item, this updates the corre-
sponding node as well.
// Update caption
_album[index].Caption = caption;

if (item != null && node != null)
{
// Update node text as well
node.Text = caption;
}
// Save the changes to the album
. . .
}
SUPPORT EDITING OF TREE NODE LABELS (continued)
Action Result
FUN WITH TREE VIEWS 521
UPDATE PROPERTIES MENU TO HANDLE TREE NODES

Action Result
1 In the MainForm.cs code
window, update the
menuProperties_Click event
handler to accommodate the
three controls that might have
the focus.
private void menuProperties_Click
(object sender, System.EventArgs e)
{
if (treeViewMain.Focused)
{
TreeNode node = treeViewMain.SelectedNode;
string file = node.Tag as string;
if (node == null || node.Parent == null
|| file == null)
return; // do nothing

if (Path.GetExtension(file) == ".abm")
DisplayAlbumProperties(node);
else
DisplayPhotoProperties(node);
}
else if (pictureBoxMain.Focused)
{
// Display photograph for this image
TreeNode node = treeViewMain.SelectedNode;
if (node != null)
DisplayPhotoProperties(node);
}
else
if (listViewMain.SelectedItems.Count > 0)
{
ListViewItem item
= listViewMain.SelectedItems[0];
if (this._albumsShown)
DisplayAlbumProperties(item);
else
DisplayPhotoProperties(item);
}
}
2 Rewrite the DisplayAlbum-
Properties method to accept
an
object instance.
private void DisplayAlbumProperties
(object obj)
{

ListViewItem item = obj as ListViewItem;
TreeNode node = obj as TreeNode;
// Open the album as appropriate
PhotoAlbum album = null;
if (item != null)
{
string fileName = item.Tag as string;
if (fileName != null)
album = this.OpenAlbum(fileName);
}
else if (node != null)
{
album = OpenTreeAlbum(node);
}
if (album == null)
. . . // as in chapter 14
How-to
a. For the TreeView control,
ignore the parent node and
call the appropriate
Proper-
ties method based on the
node type.
b. For the
PictureBox control,
call the
DisplayPhoto-
Properties method on the
selected photo node.
c. For the

ListView control,
the code is the same as in
chapter 14.
How-to
a. Convert the given object to
a
ListViewItem and a
TreeNode instance.
b. Open the
PhotoAlbum using
whichever object is not
null.
c. If the album could not be
opened, display an error
message.
522 CHAPTER 15 TREE VIEWS
3 When displaying the album edit
dialog, only update the list item
settings if the given item is a list
view item.
Note: If the given item is a tree
node, then photographs are dis-
played in the list view, and
these settings should not be
updated.
using (AlbumEditDlg dlg
= new AlbumEditDlg(album))
{
if (dlg.ShowDialog() == DialogResult.OK)
{

// Save changes made by the user
. . .
// Update item settings
if (item != null)
{
item.SubItems[MainForm.
AlbumTitleColumn].Text
= album.Title;
bool hasPwd = (album.Password != null)
&& (album.Password.Length > 0);
item.SubItems[MainForm.
AlbumPwdColumn].Text
= (hasPwd ? "y" : "n");
}
}
}
album.Dispose();
}
4 Modify the
DisplayPhotoProperties
method to accept an
object
instance.
How-to
This is similar, at least in spirit, to
the
DisplayAlbumProperties
method.
private void DisplayPhotoProperties
(object obj)

{
ListViewItem item = obj as ListViewItem;
TreeNode node = obj as TreeNode;
int index = 0;
if (item != null && (item.Tag is int))
{
index = item.Tag;
}
else if (node != null)
{
index = node.Index;
}
_album.CurrentPosition = index;
UPDATE PROPERTIES MENU TO HANDLE TREE NODES (continued)

Action Result
FUN WITH TREE VIEWS 523
As you can see, the display methods use the
as keyword to convert a given object
into both a ListViewItem and a TreeNode instance. Whichever instance is non-
null indicates how to display the property dialog.
TRY IT!
As a further change to our TreeView control, add a context menu to this
control to perform the following tasks.
1 An “Add Directory” menu item that permits a new album directory to
be added to the tree. This should prompt for a directory name and add
a top-level node to the tree for each album discovered in that directory.
2 A “Properties” menu item that displays the properties dialog for the
nearest node. This should select the nearby node, and then call the
PerformClick method for the menuProperties menu.

3 A “Delete” menu item that deletes a node from the tree. This should
delete the album file from the file system or the
Photograph from the
containing album for the given node. You should prompt the user to
make sure they really wish to do this.
You will need to use the GetNodeAt method to locate the TreeNode instance at a
given pixel position, so that the action applies to the specific tree node located at the
current mouse position.
5 After displaying the dialog,
update the list or node with any
modified photograph settings.
Note: Recall that our photo edit
dialog permits all photographs
in an album to be updated. As a
result, when the photographs
are shown in the tree node, the
label for each related node must
be updated as well. This is true
regardless of the type
object
given.
using (PhotoEditDlg dlg
= new PhotoEditDlg(_album))
{
if (dlg.ShowDialog() == DialogResult.OK)
{
// Save any changes made
. . .
// Update controls with new settings
TreeNode baseNode = null;

if (item != null)
{
LoadPhotoData(_album);
baseNode = treeViewMain.SelectedNode;
}
else if (node != null)
{
baseNode = node.Parent;
}
if (baseNode != null)
{
// Update all child labels
foreach (TreeNode n in baseNode.Nodes)
{
n.Text = _album[n.Index].Caption;
}
}
}
}
}
UPDATE PROPERTIES MENU TO HANDLE TREE NODES (continued)

Action Result
524 CHAPTER 15 TREE VIEWS
You could also implement these items within the ListView control as well.
This completes our discussion on the
TreeView class. Before we move on, let’s do a
quick recap of what we covered in this chapter.
15.6 RECAP
In this chapter we extended the MyAlbumExplorer project built in chapter 14 to add

a
TreeView control. We divided our main window using the Splitter class in
order to create a classic explorer window such as that used in the Windows operating
system for browsing the file system.
A tree view contains a hierarchy of
TreeNode objects, and we created a tree dis-
playing our album files and the photos in each album. We discussed common oper-
ations within a tree view such as expand, collapse, selection, and label editing. During
the course of the chapter, the
ListView and TreeView controls were integrated to
display a common interface, with changes to one control reflected in the other control.
We also added a
PictureBox control in order to display the image associated with
a selected photograph node in the tree.
The explorer interface we saw in these last two chapters is one of three kinds of
standard Windows interfaces. In part 2 of this book we built what is called a single
document interface. In the next chapter we will look at another kind of interface,
namely the multiple document interface.
525
CHAPTER 16
Multiple document
interfaces
16.1 Interface styles 526
16.2 MDI forms 530
16.3 Merged menus 535
16.4 MDI children 543
16.5 MDI child window management 557
16.6 Recap 563
The ListView and TreeView classes discussed in chapters 14 and 15 present a col-
lection of objects within a single list or tree control. These are especially useful when

creating an explorer-style interface such as our MyAlbumExplorer application, or the
common Windows Explorer application. Another kind of interface is the multiple
document interface, also called an MDI (normally pronounced em-dee-eye).
An MDI application presents a collection of forms within a single application
window. We will discuss MDI applications through the following discussion areas:
• Understanding various interface styles.
• Creating an MDI container window.
• Converting an SDI application into an MDI application.
• Using MDI-related class members of various controls.
• Merging two menus into a single merged menu.
• Managing menus and forms in an MDI application.
These topics will be covered as we progress through the chapter, beginning with the
concept of interface styles.
526 CHAPTER 16 MULTIPLE DOCUMENT INTERFACES
16.1 INTERFACE STYLES
Before we discuss exactly how multiple document interfaces are created, let’s take a
step back and consider the various types of application interfaces used for Windows
applications. Most Windows applications fall into one of three interface categories:
• Single document interfaces.
• Explorer interfaces.
• Multiple document interfaces.
We will discuss each type of interface separately.
16.1.1 S
INGLE DOCUMENT INTERFACES
A single document interface, also called an SDI, is an interface that displays a single
document or other encapsulated data within a single form. Our MyPhotos applica-
tion, as shown in figure 16.1, is a good example of this style, in which a single photo
album is displayed. The user can look at multiple photo albums only by examining
one after another. The contents of two albums cannot be compared unless two copies
of the program are running.

In the Windows operation system, the Notepad and WordPad applications pro-
vide additional examples of the SDI style.
16.1.2 E
XPLORER INTERFACES
The MyAlbumExplorer application built in chapters 14 and 15 is an example of an
explorer interface, and can be seen in figure 16.2. In this style, a hierarchy of informa-
tion is presented to the user. Normally a
TreeView control displays this hierarchy,
typically on the left, with details on the selected node provided in a
ListView con-
trol. Sometimes the
TreeView control can be hidden, and sometimes it is always
Figure 16.1
Our single document
interface displays one
photo album at a time.
INTERFACE STYLES 527
present. Alternate information may appear on the list side of the window as well, such
as the photographic image we displayed in chapter 15 for a selected photograph in
the MyAlbumExplorer application.
In Windows, of course, the Windows Explorer application is another example of
this style.
16.1.3 M
ULTIPLE DOCUMENT INTERFACES
A multiple document interface (MDI) allows multiple views of one or more docu-
ments or other encapsulated data to be displayed at the same type. This permits alter-
nate views of the same data, or separate presentations of the same style of data, within
a single window. For example, a stock market MDI application might present differ-
ent historical or graphical views of a single portfolio, each as a separate window. Alter-
nately, such an application might present multiple portfolios, each as its own window

within a containing application window.
In the original conception of this style, a single window acted as a container for
other windows, where each contained window displayed a specific instance or view of
a type of data. More recently, well-known MDI applications such as Microsoft Word
and Excel have taken the approach of displaying all of their windows directly on the
desktop, each within a separate application window, while still preserving an MDI
look and feel from the menu bar and other parts of the interface. This relatively new
style, the Multiple Single Document Interface, or MSDI, is consistent with the man-
ner in which Web browsers have typically worked. While an MSDI interface can be
created in Visual Studio.NET, it is not necessarily an easy task.
Figure 16.2 Our explorer interface presents the collection of photo albums in list form.
528 CHAPTER 16 MULTIPLE DOCUMENT INTERFACES
Also note that Visual Studio .NET, while providing an MDI-like interface, uses
more of a
TabControl look and feel for the set of displayed windows, or what might
be called a Multiple Tabbed Documents Interface, or MTDI. In this style, multiple
sets of windows are displayed as horizontal or vertical groups of tabs. Both the MSDI
and MTDI approaches can be created using the .NET Framework as an alternative to
the traditional MDI interface, although there is not really any direct support for these
newer interfaces. As a result, implementing such interfaces requires much more effort
from the developer.
For our purposes, a traditional MDI application provides the means to discuss
and demonstrate the manner in which the .NET Framework supports such applica-
tions. We will convert the existing MyPhotos application into the MDI application
shown in figure 16.3. As you can see, this application will incorporate the
Form classes
we have created in part 2 of this book.
The reuse of our existing classes is possible because of the manner in which the
Form
class in general and MDI support in particular is integrated into the Windows Forms

hierarchy. As we discussed in chapter 7, a
Form object is a Control instance that
happens to display an application window. For MDI applications,
Form controls are
contained by a parent
Form. Of course, the contained forms can be resized and
moved within their container, and can still display menus, toolbars, status bars, and
other controls. As we shall see, the relationship between MDI parent and child forms
is different than the relationship between control containers and controls.
Figure 16.3 Our multiple document interface, created in this chapter, displays a se-
lected set of photo albums within a single window.
INTERFACE STYLES 529
16.1.4 S
UPPORT IN WINDOWS FORMS
To provide some insight and perhaps some perspective on MDI applications, the fol-
lowing table lists a number of class members specific to the implementation of MDI
applications in the .NET Framework. Of course, these members can be used for
other purposes, and additional properties, methods, and events are certainly used in
MDI applications. These events highlight many of the MDI-specific tasks that are
often performed in this style interface. The table provides a short description of each
member and a reference to the section in this chapter where more information on
each item may be found.
Class members often used in MDI applications
Class
Member
type
Member name Description
See
section
Form

Properties ActiveMdiChild Gets the MDI child window that is currently
active.
16.4.1
IsMdiChild Gets whether the form is an MDI child. 16.3.2
IsMdiContainer Gets whether the form is an MDI container
form.
16.2.1
MdiChildren Gets the set of MDI children contained by
this form as an array of
Form objects.
16.4.3
MdiParent Gets or sets the MDI container for this form.
If set, then this form is an MDI child form.
16.2.2
MergedMenu Gets the
MainMenu object representing the
current merged menu for an MDI container
form.
16.3
Methods LayoutMdi Arranges the MDI children within this form
using a given layout style.
16.5.1
Events MdiChildActivate Occurs when an MDI child form is activated
or deactivated within an MDI application.
Note that MDI children do not receive the
Activated and Deactivate events.
16.4.4
Menu
Properties MdiListItem Gets the
MenuItem object contained by this

menu that displays a list of MDI child forms
for the associated form object.
16.5.2
Methods MergeMenu Merges the
MenuItem objects in a given
menu with those contained by this menu.
16.3
MenuItem
Properties MdiList Gets or sets whether this menu should be
populated with a list of MDI child forms
contained by the associated form.
16.5.2
MergeOrder Gets or sets the relative position of this menu
item when it is merged with another menu.
16.3.2
MergeType Gets or sets how this menu should be
merged with other menus. The default is
MergeType.Add.
16.3.1
530 CHAPTER 16 MULTIPLE DOCUMENT INTERFACES
Also note that the behaviors of desktop-related actions within an MDI child form are
modified. For example, the Minimize and Maximize buttons on the title bar work
within the parent window, rather than on the desktop itself.
In the rest of this chapter we will enhance our MyPhotos application to support
a multiple document interface. We begin with the MDI container form.
16.2 MDI FORMS
So let’s convert our existing MyPhotos application into an MDI application. This ini-
tial work is not as difficult as you might think. Generally, we need one
Form to act as
the top-level container, and the ability to create other forms as children within this

container. Here, we will do this via the following tasks:
1 Create a new parent form for the application to act as the MDI container.
2 Add a menu bar and New menu item to create MDI child forms.
3 Define a new Main method in the parent as the entry point for the application.
Of course, there will be other work to perform to clean up the behavior of our appli-
cation. These steps will get us going, and subsequent sections will deal with other
required changes. Figure 16.4 shows how our application will look by the end of this
section. Note in particular the two File menus. We will address this issue in the next
section while discussing Merged Menus.
Figure 16.4 Note the two File menus for this window. The menus from both
our ParentForm and MainForm classes appear separately on the menu bar. We
will address this in section 16.3.
MDI FORMS 531
16.2.1 C
REATING AN MDI CONTAINER FORM
The creation of an MDI container form is much like the creation of any other form.
Such a form is often referred to as a parent form, since it acts as the parent for one or
more MDI child forms. The following table details the steps required for this task.
Set the version number of the MyPhotos application to 16.2.
As you can see, the contents of the window appear in a darker color and includes a 3-
D border to indicate that this form is now an MDI container. This color is the
Sys-
tem.AppWorkspace
color, which is typically a darker version of the System.Con-
trol
color. This background is a hidden MdiClient control, and cannot be
manipulated in code as it is not exposed by the
Form class. This background contains
the MDI child forms, and is always last in the z-order. As a result, any controls added
to the form will appear above this background, and therefore in front of any MDI

children. Typically, controls added to an MDI container are docked to one edge of
the parent form.
The code generated for our
ParentForm class is much like other forms we have
seen in this book. The
InitializeComponent method generated by Visual Studio
.NET is as follows:
private void InitializeComponent()
{
//
// ParentForm
//
this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
this.ClientSize = new System.Drawing.Size(592, 373);
CREATE A NEW FORM AS AN MDI CONTAINER

Action Result
1 In the Solution Explorer window,
add a new Windows Form to the
application called
ParentForm.
The new file appears in the Solution Explorer window and
the ParentForm.cs [Design] window is displayed.
2 Set the icon property for the
form to the “icons/Writing/
BOOKS04.ICO” file in the
common image directory.
3 Set the
IsMdiContainer
property to

true.
Note: This establishes the form
as an MDI container form.
4 Set the
Size property to
600×400 pixels.
532 CHAPTER 16 MULTIPLE DOCUMENT INTERFACES
this.IsMdiContainer = true;
this.Name = “ParentForm”;
this.Text = “ParentForm”;
}
With the parent form created, we can turn our attention to the child form.
16.2.2 C
REATING AN MDI CHILD FORM
With our MDI container in place, we can add the infrastructure required for generat-
ing MDI child forms. This will consist of a menu bar and a New menu item. Fortu-
nately, we already have our
MainForm class available to act as the child form.
The following table shows how to create a child form in our application. As part
of this task, we will add an Exit menu as well.
ADD ABILITY TO CREATE CHILD FORMS

Action Result
1 Add a
MainMenu object to the ParentForm
class in the ParentForm.cs [Design] window.
2 Add a top-level File menu containing the
three menu items as shown.
3 Add a
Click event handler for the Exit

menu to close the form.
private void menuExit_Click
(object sender, System.EventArgs e)
{
Close();
}
4 Add a Click event handler for the New
menu.
private void menuNew_Click
(object sender, System.EventArgs e)
{
Settings
Menu Property Value
File (Name) menuFile
Text &File
New (Name) menuNew
Shortcut CtrlN
Tex t &N ew
separator
Exit (Name) menuExit
Text E&xit
MDI FORMS 533
That’s all it takes to create a child form. You have almost created your first MDI
application.
If you compile and run the application, you will note that the MyPhotos appli-
cation runs exactly as before. This is because the
MainForm.Main method is still the
entry point for the application, and it displays the
MainForm object using the Appli-
cation.Run

method. To fix this, we need to display the ParentForm class in the
entry point for the application. This is our next subject.
16.2.3 A
DDING A NEW ENTRY POINT
One quite simple means to fix our entry point would be to modify the Main method
in the
MainForm class directly. The new code would look as follows, with the change
highlighted in bold:
public class MainForm : System.Windows.Forms.Form
{
. . .
[STAThread]
static void Main()
{
Application.Run(new ParentForm());
}
. . .
}
While this code would do exactly what we want, a drawback of this change is that we
could no longer compile the application as the single document interface we created in
chapter 13. To preserve this ability, we will instead create a
Main method as part of the
ParentForm class, and modify the project to use this new method as the entry point.
The following table creates a new entry point within the
ParentForm class.
5 Within this handler, create a MainForm
object as an MDI child form.
MainForm newChild = new MainForm();
newChild.MdiParent = this;
newChild.Show();

}
ADD ABILITY TO CREATE CHILD FORMS (continued)

Action Result
How-to
a. Create a new MainForm object.
b. Define this form as an MDI child by set-
ting the current form as its MDI parent.
c. Display the child form using the
Show
method.
534 CHAPTER 16 MULTIPLE DOCUMENT INTERFACES
The application is now ready. The startup object specified here is used by the C#
compiler to establish the entry point for the application, and is only required if there
are multiple
Main methods in your project. On the command-line, the C# compiler
accepts the
/main switch to specify the class containing the Main method to use as
the application’s entry point.
Run the application to verify that the
ParentForm window appears and the New
menu can be used to create
MainForm objects as child windows. If you explore this new
application, you will find some rather peculiar behavior for some of the controls. We
will discuss and address these issues throughout the remainder of this chapter.
TRY IT!
Of course, the MyPhotos Property Pages dialog used in step 2 can also be
used to set the Startup Object to the
MyPhotos.MainForm class. When
this is done, the application displays the familiar single document interface

created in part 2 of this book. Make this change and run the application to
observe this behavior.
Among the odd features you may notice in the MDI version of this application is the
menu bar. In particular, there are two File menus when a
MainForm window is dis-
played. Adjusting this behavior is our next topic.
CREATE AN ENTRY POINT IN THE PARENT FORM

Action Result
1 Create a
Main method in the
ParentForm.cs code window to
serve as the entry point for our
MDI application.
Note: If you compile the applica-
tion after this step, you will get an
error indicating that the program
defines more than one entry
point.
/// <summary>
/// Entry point for MDI application.
/// </summary>
[STAThread]
static void Main()
{
Application.Run(new ParentForm());
}
2 Set the Startup Object for the
MyPhotos project to the
MyPhotos.ParentForm class.

How-to
a. Display the Property Pages dia-
log for the project.
b. Click the down arrow associ-
ated with the Startup Object
entry.
c. Select the
MyPhotos.Parent-
Form class.
MERGED MENUS 535
16.3 MERGED MENUS
By definition, an MDI application permits multiple windows to be displayed. Each
child window may be the same or different, and each may display different informa-
tion about one or more objects. It would be nice if the menu items for the application
could be customized depending on which child window is displayed. Exactly how to
do this is the subject of this section.
As an example, consider a car-buying application that permits users to search for,
display, and purchase used cars. As an MDI application, this might display a photo-
graph of the car in one window, standard features and warranty information in
another window, and optional packages and pricing information in a third window.
Clearly the set of menus and the contents of each menu should differ depending on
which style window is currently active. For instance, menus for the photograph win-
dow might permit different colors to be viewed or different parts of the vehicle to be
shown. These concepts make no sense for the other windows, and should not be acces-
sible when these windows are active.
While our application is not quite so ambitious, we do have the problem of our
File menu, since both the
ParentForm and the MainForm class contain this item.
Once we make the two File menus merge, we also have to deal with the contents of
these menus, to ensure the items appear in an appropriate order.

The
Menu class provides a MergeMenu method for merging two menus together.
This method accepts a
Menu object and merges it with the calling Menu object. The
MenuItem class provides additional overrides of this method to merge MenuItem
objects and to copy a menu item so that it may be merged with other menus. This lat-
ter method has the advantage of not affecting the existing
MenuItem object.
In MDI applications, an MDI container form automatically merges the menu for
the active child form with the
MainMenu object stored in its Menu property. The
Form.MergedMenu property contains the result of this merge, and can be used to
access or modify the merged menu directly. The
Form.Menu property always contains
the original menu assigned to the form.
Since this merging occurs automatically for MDI applications, this section will
focus on how menus are merged together, and make the appropriate changes in our
MDI application to merge the two File menus together. First we will discuss the var-
ious ways to merge two menus, followed by the mechanism for establishing the order
of merged menu items.
16.3.1 A
SSIGNING MERGE TYPES
As mentioned at the start of this chapter, the MenuItem class contains two properties
that control exactly how two menus are merged together. This section will discuss the
MergeType property that controls how the menus are merged. Later we will look at
the
MergeOrder property that controls the final position of a merged item.
536 CHAPTER 16 MULTIPLE DOCUMENT INTERFACES
The MergeType property gets or sets a MenuMerge enumeration value specify-
ing how this menu should be merged with other menus. An overview of this enumer-

ation appears in .NET Table 16.1. The default setting for the
MergeType property
is
MenuMerge.Add. This default adds each item separately, and is the cause of the two
File menus in our current application.
This explains why our existing application has two File menus. Since the
MergeType
property defaults to Add, the menus are simply added to the collection separately.
We can fix this by modifying the
MergeType property for these menus.
Set the version number of the MyPhotos application to 16.3.
Compile and run the application, and open a client form in the parent window to see
the merged menu as shown in the table. The two menus are merged, but their contents
.NET Table 16.1 MenuMerge enumeration
The MenuMerge enumeration specifies various types of behavior for a MenuItem object
when it is merged with another menu. This enumeration is used by the
MergeType property
in the
MenuItem class, and is part of the System.Windows.Forms namespace.
Enumeration
Values
Add The item is added to the collection of
MenuItem
objects in the merged menu.
MergeItems All
MenuItem objects contained by the item are
merged with those contained by the menu at the
same position in the merged menu.
Remove The item is not included in the merged menu.
Replace The item replaces an existing

MenuItem object
at the same position in the merged menu.
MERGE THE PARENT AND CHILD FILE MENUS

Action Result
1 In the MainForm.cs [Design]
window, set the
MergeType
property of the File menu item
to
MergeItems.
The two File menus in the parent and child form will now
merge into a single menu in the application, the result of
which is shown in this graphic. The menu items exhibit
the default merge behavior, which is
Add.
2 Similarly, set the
MergeType
property to
MergeItems for the
File menu in the ParentForm.cs
[Design] window.
Note: The
MergeType property
must be set for both File menu
objects to merge the two
menus together.
MERGED MENUS 537
are not exactly in an acceptable order. This is because each of the
MenuItem objects

within their respective File menus use the default
MergeType property, which is Add.
As a result, each menu item is simply added to the end of the list. The items from the
parent form appear first, followed by the items from the child form.
We can fix this, of course, but first a brief aside.
TRY IT!
Modify the MergeType property for either File menu so that one menu
uses the
MergeItems member value and the other the Add value. Run the
application to verify that the menus no longer merge.
Also rename the File menu in the
ParentForm class to use the name
“Fickle.” Run the application and see which name is shown in the applica-
tion. You will find that the name in the MDI child is preferred over the
name in the parent. This is a consequence of how the menus are merged,
and can be utilized to rename a menu in the parent form when a specific
kind of child is displayed.
Back in our application, we have two problems with the merged File menu. The first
is that we have two versions of the New and Exit menus, and the second is that the
order of the merged menu is a bit of a mess.
We will address these two problems together as part of a discussion on the
Merge-
Order
property.
16.3.2 A
SSIGNING MERGE ORDER
So far we have merged our two File menus into a single menu. The next step is to
clean up the contents of this menu. This involves setting the appropriate
MergeType
for each menu, and using the MergeOrder property to establish the order of these

items within the merged menu. The
MergeOrder property contains the zero-based
position where the menu should appear within the merged menu. If multiple items
are assigned the same order, they appear one after another in the merged menu. This
is the case in our existing code, where all menus in the File menu use the default
MergeOrder value of zero.
Before we start making changes to our existing menus, let’s step back and consider
what a reasonable File menu should contain for our MDI application. Such a menu
is described by the following table, which shows the menu name, its position, a short
description, and some implementation notes.
Contents of the merged File menu in our MDI application
Menu name Position Description Implementation Notes
New 0 Opens a new album in a new MDI
child window.
Same as existing New menu in the
ParentForm class.
Open 1 Opens an existing album file in a
new MDI child window.
This should be processed by the
ParentForm class in order to create
the new child window.
538 CHAPTER 16 MULTIPLE DOCUMENT INTERFACES
This details how the merged menu should look. There is still the question of the
menu structure in the
ParentForm and MainForm classes. Based on the previous
table, we can establish how the File menu should appear for each
Form class. The fol-
lowing table details the contents of each menu, and describes its behavior in the
merged menu object.
We are now ready to update our menus based on these tables. Our first change will

simply update the menus so that they appear as described within the application.
Close 2 Closes the active MDI child
window.
Similar to the Exit menu in the
MainForm class.
separator 3
Save 4 Saves the album in the active MDI
child window.
Same as existing Save menu in the
MainForm class
Save As 5 Saves the album in the active MDI
child window under a new name.
Same as existing Save As menu in
the
MainForm class.
separator 6
Exit 7 Closes all child windows as well as
the MDI container form.
Same as existing Exit menu in the
ParentForm class.
Individual File menu for our MDI parent and child classes
Class Menu Implementation notes
ParentForm
New This menu should behave as it already does, and replace the New menu
in the child form.
Open This is a new menu to open an existing album in a new window.
separator This menu should not exist when the menus are merged.
Exit This menu should behave as it already does, and appear at position 7
when the menus are merged.
MainForm

New Should not be present in the merged menu for our MDI application.
Open Should not be present in the merged menu.
separator Should become the first separator at position 3 in the merged menu.
Save As currently exists, at position 4 in the merged menu.
Save As As currently exists, at position 5 in the merged menu.
separator Should become the second separator at position 6 in the merged menu.
Exit Should become the Close menu at position 2 in the merged menu.
Contents of the merged File menu in our MDI application (continued)
Menu name Position Description Implementation Notes
MERGED MENUS 539
Once this is done, we will look at implementing any changes required to support
these menus.
The following table details the steps required:
The key points here are the fact that the New and Open menus in the
ParentForm
class replace those in the MainForm class, and the merge order for each menu must
match the desired position we discussed earlier. One other interesting point is the
reuse of the Exit menu in the
MainForm class for the Close menu in the merged
menu. This makes sense, although we still need to rename the menu text to read
ASSIGN THE TYPE AND ORDER FOR OUR FILE MENUS

Action Result
1 In the ParentForm.cs [Design]
window, add an Open menu to the
File menu just after the existing New
menu.
2 Update the merge settings for the
items in the File menu.
3 In the File menu for the MainForm.cs

[Design] window, update the merge
settings for the items in this menu.
Settings
Property Value
(Name) menuOpen
Shortcut CtrlO
Text &Open
Settings
Menu MergeType MergeOrder
New Replace 0
Open Replace 1
separator Remove 0
Exit Add 7
Settings
Menu MergeType MergeOrder
New Remove 0
Open Remove 1
separator Add 3
Save Add 4
Save As Add 5
separator Add 6
Exit Add 2
540 CHAPTER 16 MULTIPLE DOCUMENT INTERFACES
“Close” rather than “Exit.” We will do this in a way that continues to preserve the
SDI application from part 2.
This change ensures that the Exit menu displays “Close” when the
MainForm object is
created as an MDI child window. Otherwise, the default setting of “Exit” will be used.
Compile and run the application to verify that our changes produce the appro-
priate menu structure. Create a new MDI child window and display the File menu.

Your application should appear as in figure 16.5. Note how all the menus are now in
the desired order, including the separator menus. Also note that the Exit menu from
the
MainForm class is reincarnated as the Close menu in the MDI application.
CHANGE THE EXIT MENU TEXT WHEN RUNNING AS AN MDI CHILD FORM

Action Result
4 Override the
OnLoad method in
the MainForm.cs code window.
protected override void OnLoad(EventArgs e)
{
5 If the form is an MDI child
window, then modify the Exit
menu to appear as a Close
menu.
How-to
Use the IsMdiChild property.
if (IsMdiChild)
menuExit.Text = "&Close";
base.OnLoad(e);
}
Figure 16.5 The merged File menu here gives no indication that different
menu items are processed in different classes.

×