Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
C# ПІДРУЧНИКИ / c# / Manning - Windows.forms.programming.with.c#.pdf
Скачиваний:
108
Добавлен:
12.02.2016
Размер:
14.98 Mб
Скачать

of an architecture is not its ability to work as designed, but rather its ability to perform tasks for which it was not designed. The coding techniques we have used throughout the book are useful in any application to accommodate future requirements. These techniques include frequent encapsulation of tasks into separate methods; sketching a user interface design or enumerating the steps required before writing any code; and building reusable libraries and methods where possible.

As a result, our code has some advantages for new changes such as this in that we have consistently tried to use good coding practices and not duplicate our tasks in multiple places. While perhaps not always successful, I believe we have done a reasonable job.

In the PixelDlg form, for example, we were careful to only update this form in the UpdatePixelData method of the MainForm class. Similarly, the only location where the PixelDlg form is created right now is in the Click event handler for the menuPixelData object. Such organization occasionally requires a little extra work, or in our case a few more pages, but this effort often pays off as the code is maintained and updated in the future.

Stepping off my soap box and returning to the topic at hand, we will make our changes in the order shown in the previous list, beginning with a global PixelDlg instance. For this we will provide a static property in the PixelDlg class that returns a shared form.

CREATE A SHARED PIXELDLG INSTANCE

 

Action

Result

 

 

 

1

In the PixelDlg.cs code window,

static private Form _mdiForm = null;

 

create static members to hold

static private PixelDlg _globalDlg;

 

the shared instance and an MDI

 

 

parent form, if any.

 

 

 

 

2

Create an internal property to

static internal Form GlobalMdiParent

 

assign and retrieve an MDI

{

 

container form.

get { return _mdiForm; }

 

set { _mdiForm = value; }

 

 

 

 

}

 

 

 

3

Create a public property to

static public PixelDlg GlobalDialog

 

retrieve the shared form.

{

 

How-to

get

 

{

 

If the current _globalDlg value

if (_globalDlg == null

 

is invalid, then create a new

|| _globalDlg.IsDisposed)

 

{

 

instance of the Form.

 

_globalDlg = new PixelDlg();

 

 

_globalDlg.MdiParent = GlobalMdiParent;

 

 

}

 

 

return _globalDlg;

 

 

}

 

 

}

 

 

 

The GlobalDialog property provides the mechanism by which all child MainForm instances can access the same PixelDlg form. Recall that our PixelDlg form is

MDI CHILDREN

549

disposed whenever the user clicks the Close button. For this reason, this property recreates the dialog whenever it is null or disposed.

The GlobalMdiParent property provides a method for turning this global dialog into an MDI child form. We can use this property in our ParentForm class to establish the MDI container for our global dialog.

SET THE MDI PARENT FOR THE GLOBAL PIXELDLG FORM

 

Action

Result

 

 

 

4

In the ParentForm.cs code

protected override void OnLoad(EventArgs e)

 

window, override the OnLoad

{

 

method to assign this form as

PixelDlg.GlobalMdiParent = this;

 

 

 

the MDI parent for the global

base.OnLoad(e);

 

PixelDlg form.

}

 

 

 

This ensures that whenever a new global dialog is created, the ParentForm object is assigned as the MDI parent. With this in place, we are ready to access the global dialog from the MainForm class. As mentioned earlier, right now the dialog is created only in the menuPixelData_Click method, so this is the only place we need to call our new property.

ACCESS THE GLOBAL PIXELDLG FROM THE MAINFORM CLASS

 

Action

Result

 

 

 

5

Locate the

private void menuPixelData_Click

 

menuPixelData_Click method

(object sender, System.EventArgs e)

 

in the MainForm.cs code

{

 

 

 

window.

 

 

 

 

6

Modify the creation of the

if (_dlgPixel == null

 

PixelDlg form to use the new

|| _dlgPixel.IsDisposed)

 

GlobalDialog property.

{

 

_dlgPixel = PixelDlg.GlobalDialog;

 

 

 

 

}

 

 

_nPixelDlgIndex = _album.CurrentPosition;

 

 

Point p = pnlPhoto.PointToClient(

 

 

Form.MousePosition);

 

 

UpdatePixelData(p.X, p.Y);

 

 

AssignPixelToggle(true);

 

 

_dlgPixel.Show();

 

 

}

 

 

 

This change simply retrieves the global dialog rather than creating a new instance. You can compile and run this if you like. You will see that our code works fine for a single MDI child window. When a second window is added, the PixelDlg form is not associated with this window, and no longer works.

550

CHAPTER 16 MULTIPLE DOCUMENT INTERFACES

There are a couple ways to fix this problem. We will do so by observing that all mouse movement in each window is processed by the pnlPhoto_MouseMove event handler. This method in turn calls the UpdatePixelData method, as does all other updates to the dialog. As a result, we can associate an existing PixelDlg form with a new window by assigning the dialog at the start of our update method.

The following steps make this change in our application.

ENSURE AN EXISTING PIXELDLG FORM IS ASSIGNED TO NEW CHILD INSTANCES

 

Action

Result

 

 

 

7

Locate the UpdatePixelData

protected void UpdatePixelData(int xPos,

 

method.

int yPos)

 

 

{

 

 

 

8

Assign the _dlgPixel field at

if (IsMdiChild)

 

the beginning of the method.

_dlgPixel = PixelDlg.GlobalDialog;

 

 

. . .

 

 

}

 

 

 

This now guarantees that a child form will pick up the global PixelDlg form as needed. Of course, this change also causes the dialog to be created even when it is not used. Such a change might not be appropriate in a large application with multiple utility forms such as our pixel dialog. For our purposes, it is okay.

Compile and run the program to verify that our new code works. Also realize that these changes are consistent with our non-MDI application. When a single MainForm instance is present, it will now use the global PixelDlg instance to create the dialog, and all code will work as we originally intended in chapter 8. You can test this by modifying the MyPhotos project settings to use the MainForm.Main method as the entry point.

The PixelDlg form is now integrated into our MDI application. The next task is to ensure that we do not open multiple windows for the same album file.

16.4.3OPENING AN ALBUM TWICE

In our current code for the Open menu, the user selects an album and a new child window is created to contain this album. This is fine as long as the selected album has not been previously opened by the user. In the case where a MainForm window already exists for the selected album, it would be more appropriate to simply display the existing window at the top of the z-order.

This can be done by searching through the list of child windows for one that displays the selected album. The MdiChildren property in the Form class retrieves the collection of child forms assigned to an MDI container form. This property can be treated like any other array to search for a matching form.

This property is useful whenever a specific form is desired, as we do here. It can also be used to see if any child forms are present in an MDI application and to obtain the number of MDI child forms, although checking the ActiveMdiChild property is typically a more efficient mechanism for the former task.

MDI CHILDREN

551

When implementing this change, we should keep in mind the fact that forms other than our MainForm class might be contained by this array. The following steps detail a solution for this change with this fact in mind.

HANDLE AN ATTEMPT TO OPEN A DISPLAYED ALBUM

 

Action

Result

 

 

 

1

In the MainForm.cs code

public string AlbumFile

 

window, add a new

{

 

AlbumFile property to

get { return _album.FileName; }

 

}

 

retrieve the file name of the

 

 

 

displayed album.

 

 

 

 

2

Locate the

private void menuOpen_Click

 

menuOpen_Click event

(object sender, System.EventArgs e)

 

handler in the ParentForm.cs

{

 

 

 

code window.

 

 

 

 

3

Before opening a new

. . .

 

MainForm window, search

if (dlg.ShowDialog() == DialogResult.OK)

 

through the set of existing

{

 

try

 

child forms.

 

{

 

 

// See if album is already open

 

 

foreach (Form f in MdiChildren)

 

 

{

 

 

 

4

If a MainForm instance is

if (f is MainForm)

 

found, see if it displays the

{

 

selected album.

MainForm mf = (MainForm) f;

 

if (mf.AlbumFile == dlg.FileName)

 

 

 

 

{

 

 

 

5

If a match is found, bring the

if (mf.WindowState

 

existing album to the front of

== FormWindowState.Minimized)

 

the application window.

{

 

mf.WindowState

 

 

 

How-to

= FormWindowState.Normal;

 

}

 

a. If the form is minimized,

 

mf.BringToFront();

 

return it to a normal state.

return;

 

b. Display the form at the

}

 

}

 

front of the MDI window.

 

}

 

 

 

6

If no matching window is

// Open new child window for album

 

found, the existing code will

MainForm form

 

create a new MainForm

= new MainForm(dlg.FileName);

 

. . .

 

object for the album.

 

}

 

 

 

This code uses some properties we have not seen before. When a matching child Form is found, the WindowState property is used to assign or retrieve the current display state of the MDI child form within its container. For a top-level form, this affects the display state on the desktop. The WindowState property takes its values from the FormWindowState enumeration, summarized in .NET Table 16.2. In the previous table, we check to see if the MDI child is minimized, and if so return it to a Normal state.

552

CHAPTER 16 MULTIPLE DOCUMENT INTERFACES

MdiChildActi-

.NET Table 16.2 FormWindowState enumeration

The FormWindowState enumeration specifies the possible display states for a Form on the desktop or within an MDI application. This enumeration is part of the System.Windows.Forms namespace.

 

Maximized

The form is maximized so that it fills the entire possible

 

 

display area, either the entire desktop or the entire display

 

 

window when the form is an MDI child form.

Enumeration

Minimized

The form is minimized and is listed in the task bar or at the

 

base of the MDI container form.

Values

 

 

 

 

Normal

The form appears in its normal state. By default, the form is

 

 

visible and not maximized. If a maximized Form is minimized,

 

 

then setting the Form to Normal will return the form to a

 

 

maximized state.

 

 

 

We also use the BringToFront method to display the form at the top of the z-order within the MDI container. This method is part of the Control class and can be used to adjust the z-order position of any Windows Forms control within its container. There is also a corresponding SendToBack method to place a control at the bottom of the z-order.

Run the application to verify this feature works. Try minimizing or maximizing the form before opening the same album to verify that the proper behavior occurs. Also verify that when a new album is selected, a new MDI child form appears as before.

This completes the three tasks we set out at the start of section 16.4. We have added a toolbar to our parent form and hidden the toolbar in the child, turned the PixelDlg form into an MDI child when running as an MDI application, and ensured that an album can only be opened within a single MDI child form.

As a final change, and to create a slightly more polished application, let’s make one more addition here to place the current album in the title bar.

16.4.4UPDATING THE TITLE BAR

An MDI application should normally update its title bar to reflect the contents of the currently active child form. This provides good feedback to your users, especially when the application is minimized and only appears in the task bar. In our case, we will also include the version number on the title bar as is our custom. The result of this change is shown in figure 16.8.

We could like to update the title bar whenever a new form is activated within the MDI container. There is an event for just this purpose, namely the

vate event. This event occurs whenever an MDI child form is closed or becomes the active form within the container.

MDI CHILDREN

553

Соседние файлы в папке c#