Application Architecture in Windows Forms 2.0—Single-MDI Applications


Jump to: navigation, search
CSharp-Online.NET:Articles
C# Articles

Appl. Architecture Forms

© 2006 Pearson Education, Inc.

Single-MDI Applications

Consider an MDI application like Microsoft Excel; files opened from the file system (by double-clicking) are all opened as separate child windows within the parent Excel window.7 For the first instance of an MDI application to open a new child window to display the file that was passed to the second instance of the application, the second instance must be able to communicate with the initial instance.

A single-MDI application exhibits the characteristics we described in Chapter 2: Forms, as well as the following features:

  • A single instance of the application is running.
  • Multiple MDI child windows are running within the same MDI parent window.
  • Currently opened files are not reopened.
  • When the last MDI child window goes away, the application remains.
  • When the MDI parent window goes away, the application exits.
  • A Window menu allows a user to see and select from the currently available windows.

The work flow for a single-MDI application ensures that a new MDI child form is opened each time the application is called, whether or not a file was requested for opening.

The first time the application is called, the MDI parent is created and set as the main application form instance; if a file was requested, it is also opened into a new MDI child form. Subsequent requests to the application are routed through the MDI parent form to create a new MDI child form and build up the appropriate menu structures to support navigation between top-level instances, as well as opening and closing existing top-level instances. Figure 14.7 illustrates the work flow.

Image:AAWFfig-14-7.jpg
Figure 14.7 Work Flow of a Single-MDI Application with Support for Passing Command Line Arguments

With WindowsFormsApplicationBase ensuring that only one instance of the application executes, we need to handle two specific scenarios: first, when arguments are passed from the command line directly when the first instance loads and, second, when the first instance is passed command line arguments from a second instance.

Handling the first scenario requires a main application form that’s an MDI parent and can open a new or existing file into an MDI child form:

// MDIParentForm.cs
partial class MDIParentForm : Form {
 ...
 // This is necessary to bring the MDI parent window to the front,
 // because Activate and BringToFront don’t seem to have any effect.
 [DllImport("user32.dll")]
 static extern bool SetForegroundWindow(IntPtr hWnd);
 
 public void CreateMDIChildWindow(string fileName) {
 
  SetForegroundWindow(this.Handle);
   
  // Detect whether file is already open
  if( !string.IsNullOrEmpty(fileName) ) {
   foreach( MDIChildForm openForm in this.MdiChildren ) {
    if( string.Compare(openForm.FileName, fileName, true) == 0 ) {
     openForm.Activate();
     return;
    }
   }
  }
 
  // If file not open, open it
  MDIChildForm form = new MDIChildForm();
  form.OpenFile(fileName);
  form.MdiParent = this;
  form.Show();
 }
 
 void newToolStripMenuItem_Click(object sender, EventArgs e) {
  this.CreateMDIChildWindow(null);
 }
 
 void openToolStripMenuItem_Click(object sender, EventArgs e) {
  if( this.openFileDialog.ShowDialog() == DialogResult.OK ) {
   this.CreateMDIChildWindow(this.openFileDialog.FileName);
  }
 }
 ...
}

This code allows users to open a file using a menu strip item, and it lays the foundation for opening a file from the command line, including preventing the reopening of an already open file. We continue using WindowsFormsApplicationBase to achieve this, updating the earlier sample to acquire the command line arguments and pass them to the application main form’s CreateMDIChildWindow method to open a file:

// SingleMDIApplication.cs
class SingleMDIApplication : WindowsFormsApplicationBase {
 
 static SingleMDIApplication application;
 internal static SingleMDIApplication Application {
  get {
   if( application == null ) {
    application = new SingleMDIApplication();
   }
   return application;
  }
 }
 
 public SingleMDIApplication() {
  // This ensures the underlying single-SDI framework is employed, 
  // and OnStartupNextInstance is fired
  this.IsSingleInstance = true;
 }
 
 // Load MDI parent form and first MDI child form
 protected override void OnCreateMainForm() {
  this.MainForm = new MDIParentForm();
  this.CreateMDIChildWindow(this.CommandLineArgs);
 }
 
 void CreateMDIChildWindow(ReadOnlyCollection<string> args) {
  // Get file name, if provided
  string fileName = (args.Count > 0 ? args[0] : null);
  
  // Ask MDI parent to create a new MDI child 
  // and open file
  ((MDIParentForm)this.MainForm).CreateMDIChildWindow(fileName);
 }
}

During construction, we specify that this application is a single-instance application. Unlike with multiple-SDI applications, however, we don’t need to set the ShutdownStyle property because its value defaults to AfterMainFormCloses—exactly what is needed for an MDI application.

OnCreateMainForm creates the MDI parent form and sets it as the application’s main form and the one responsible for creating MDI child windows. Then, the command line arguments are passed to the helper CreateMDIChildWindow method, which parses them for a file name. Either a file name or null is passed to the MDI parent form’s version of CreateMDIChildWindow, which creates the new MDI child window, into which it loads a file; then CreateMDIChildWindow establishes the MDI parent-child relationship and shows the requested file. CreateMDIChildWindow also activates the MDI parent form to bring the application to the foreground.

In the second scenario, the desired processing is for the command line arguments to be passed from the second instance to the first, to which the first instance responds by processing the command line arguments and, if required, creating a new MDI child form. WindowsFormsApplicationBase handles the underlying mechanics of passing arguments from the second instance to the first, but it is up to you to process the command line arguments accordingly. You can achieve this by overriding WindowsFormsApplicationBase.OnStartupNextInstance, which passes the command line arguments via the CommandLine property of a StartupNextInstanceEventArgs object. The following code shows the OnStartupNextInstance override implementation:

// SingleMDIApplication.cs
class SingleMDIApplication : WindowsFormsApplicationBase {
 ...
 // Must call base constructor to ensure correct initial 
 // WindowsFormsApplicationBase configuration
 public SingleMDIApplication() {...}
 
 // Load MDI parent form and first MDI child form
 protected override void OnCreateMainForm() {...}
 
 // Load subsequent MDI child form
 protected override void OnStartupNextInstance(
  StartupNextInstanceEventArgs e) {
  this.CreateMDIChildWindow (e.CommandLine);
 }
 
 void CreateMDIChildWindow(ReadOnlyCollection<string> args) {...}
}

As you can see, centralizing CreateMDIChildWindow into a single helper method greatly simplifies the implementation of OnStartupNextInstance.

That’s the complete solution, so let’s look at how it operates. Suppose we start the application for the first time by executing the following statement from the command line:

C:\SingleInstanceSample.exe C:\file1.txt

The result is to load the application, configure the single-instance command line argument (passing support from our derivation of WindowsFormsApplicationBase), load the main MDI parent form, and, finally, open an MDI child form, displaying the file specified from the command line arguments. Figure 14.8 illustrates the result.

Image:AAWFfig-14-8.jpg
Figure 14.8 Result of Creating a First Instance of a Single-Instance Application

Now, consider the next statement being called while the first instance is still executing:

C:\SingleInstanceSample.exe C:\file2.txt

This time, a second instance of the application is created, but—thanks to SingleMDIApplication, our WindowsFormsApplicationBase derivation—the second instance passes its command line arguments to the first instance before closing itself down. The first instance processes the incoming command line arguments from OnStartupNextInstance, requesting the MDI parent form to open a new MDI child and display the specified file. The result is shown in Figure 14.9.

Image:AAWFfig-14-9.jpg
Figure 14.9 Result of Creating a Second Instance of a Single-Instance Application

Although it would be difficult to code single-instance applications such as single MDI and multiple SDI by hand, the presence of support in the Visual Basic runtime assembly makes life a lot easier. This is one of the strengths of Windows Forms; unlike forms packages of old, Windows Forms is only one part of a much larger, integrated whole. When its windowing classes don’t meet your needs, you still have all the rest of the .NET Framework Class Library to fall back on.


Previous_Page_.gif Next_Page_.gif


Personal tools