The FileDocument Component


The FileDocument component comes with the source code for this book and was built specifically to provide MFC-style document management to your Windows Forms SDI and MDI applications. When it is added to the Toolbox, you can drag it onto a form and use the Properties window to set the properties as appropriate, as shown for the SDI Rates of Return application in Figure F.2.

Figure F.2. Managing a FileDocument Component in the Properties Window


As you can see, a wide variety of document management and shell integration support can be configured from the Properties window. Let's see how it works.

Dirty Bit Management

Noticing when the document has changed is an application-specific task, but storing whether it's been changed is application-generic, typically stored in a Boolean called the dirty bit. In our sample, we track when the data has changed in the data source's ListChanged event and set the FileDocument component's dirty bit:

// RatesOfReturnForm.cs partial class RatesOfReturnForm : Form {    ...    void PeriodReturnBindingSource_ListChanged(      object sender, ListChangedEventArgs e) {      ...      // Update the dirty bit      this.fileDocument.Dirty = true;      ...    }    ... }


The FileDocument component updates the caption text for the hosting form to reflect the dirty bit, as shown in Figure F.3.[4]

[4] The FileDocument component knows about the hosting form via techniques described in Chapter 11: Design-Time Integration: The Properties Window.

Figure F.3. Dirty SDI Application Data


Of course, if you want to let users know that their data is dirty, you need to provide enough file management support to allow them to save their dirty data to disk and to reload it.

File Management

To provide file management support, the FileDocument component provides a DefaultFileName property to specify a default file name; FileExtension and FileExtensionDescription properties to set the file dialog properties appropriately for custom file extensions; and a RegisterFileExtensionWithShell property to register the extension so that double-clicking a custom application file launches the application from the shell.

After your application has been run once, thereby allowing the FileDocument component to register your file extension with the shell, double-clicking one of your application's document files will launch a new instance of the application and open the desired file.[5] To manage that, our sample handles retrieving a file name from the command line arguments:

[5] To limit your application to a single instance, see the discussion in Chapter 14: Applications.

// Program.cs [STAThread] static void Main(string[] args) {   Application.EnableVisualStyles();   // Load main form, taking command line into account   RatesOfReturnForm form = new RatesOfReturnForm();   if( args.Length == 1 ) {     form.OpenDocument(Path.GetFullPath(args[0]));   }   Application.Run(form); }


The main form implementation of the OpenDocument method passes the file name to the FileDocument component:

// RatesOfReturnForm.cs partial class RatesOfReturnForm : Form {    ...    // For opening document from command line arguments    public bool OpenDocument(string filename) {      return this.fileDocument.Open(filename);    }    ... }


When the FileDocument object is first created or cleared through the New method (as an implementation of the File | New menu item would do), FileDocument fires the NewDocument event, which we use to set the initial seed data on our form:

class RatesOfReturnForm : Form {    ...    void fileDocument_NewDocument(object sender, EventArgs e) {      // Reset list      this.periodReturns.Clear();      this.periodReturns.Add(new PeriodReturn("start", 0M, 1000M));    }    ... }


On the other hand, if a file is passed through the command line or if we call the Open method on the FileDocument (as we would when implementing the File | Open menu strip item), the FileDocument fires the ReadDocument event, which is an excellent place to deserialize the contents of a file:

// RatesOfReturnForm.cs partial class RatesOfReturnForm : Form {    ...    void fileDocument_ReadDocument(      object sender, SerializeDocumentEventArgs e) {      // Deserialize object      IFormatter formatter = new BinaryFormatter();      this.periodReturns =        (BindingList<PeriodReturn>)formatter.Deserialize(e.Stream);      this.PeriodReturnBindingSource.DataSource = this.periodReturns;    }    ... }


The ReadDocument event passes an object of the custom type SerializeDocumentEventArgs, which contains the file name of the document to be read and a stream already opened on that file. When you ask the FileDocument to open, it checks the dirty bit to see whether the current document needs to be saved first, prompts the user, saves the document as necessary, uses the FileExtension to show the file open dialog, gets the file name, updates the hosting form's caption with the new file name, and even puts the newly opened file into the shell's Start | Documents menu. The FileDocument component asks us to do only the small application-specific part (reading the data from the stream) by firing the ReadDocument event at the right stage in the process.

In the same way, saving is a matter of handling the WriteDocument event:

// RatesOfReturnForm.cs partial class RatesOfReturnForm : Form {    ...    void fileDocument_WriteDocument(      object sender, SerializeDocumentEventArgs e) {      // Serialize object      IFormatter formatter = new BinaryFormatter();      formatter.Serialize(e.Stream, this.periodReturns);    }    ... }


Just like Open, the FileDocument component handles all the chores of the Save family of operations, including the slightly different semantics of Save, Save As, and Save Copy As. The component also makes sure to change the current file and the dirty bit as appropriate, asking the application to do only the application-specific serialization.

Handling the File Menu Items

The NewDocument, ReadDocument, and WriteDocument events are called as part of the implementation of the File menu strip items. You could handle the menu strip item clicks by calling the corresponding FileDocument methods:

// RatesOfReturnForm.cs partial class RatesOfReturnForm : Form {   ...   void newToolStripMenuItem_Click(object sender, EventArgs e) {     // You really don't need to do this...     this.fileDocument.New();   }   void openToolStripMenuItem_Click(object sender, EventArgs e) {     // or this...     this.fileDocument.Open();   }   void saveToolStripMenuItem_Click(object sender, EventArgs e) {     // or this...     this.fileDocument.Save();   }   void saveAsToolStripMenuItem_Click(object sender, EventArgs e) {     // or this...     this.fileDocument.SaveAs();   }    void saveCopyAsToolStripMenuItem_Click(object sender, EventArgs e) {      // or even this...      this.fileDocument.SaveCopyAs();    }    ... }


Because FileDocument knows that this is the kind of thing you're likely to do, it lets you select the appropriate menu strip item in the Properties window from a drop-down list, as shown in Figure F.4, handling the menu items for you.

Figure F.4. Letting the FileDocument Handle the File Menu Strip Items


The FileDocument component also provides equivalent tool strip integration for associating FileDocument actions with tool strip buttons.

Notice that File | Exit isn't on the menu integration list (nor is it on the tool strip integration list). It is up to the form to implement Exit:

// RatesOfReturnForm.cs partial class RatesOfReturnForm : Form {    ...    void exitToolStripMenuItem_Click(object sender, EventArgs e) {      // Let FileDocument component decide whether this is OK      this.Close();    }    ... }


All the main form has to do to implement File | Exit is to do what it normally would in any application: close itself. Because FileDocument knows which form is hosting it, it can handle the main form's Closing event and let it close or not based on the dirty bit and users' preferences for saving their data; you don't need to write any special code to make this happen.

MDI and the FileDocument Component

The MDI use of the FileDocument component is nearly identical to the SDI case, except that the File menu options are split between the child and the parent.[6] The MDI parent implements File | New, File | Open, and File | Exit, and the MDI child form provides the File | Save family of menu items and the File | Close menu item. The File | Save menu items are implemented in the MDI child exactly as they'd be implemented in an SDI form hosting a FileDocument component, whereas the File | Close menu item is implemented by hooking up the FileCloseMenuItem property, as shown in Figure F.5.

[6] The construction of MDI applications in Windows Forms, including menu merging, is discussed in Chapter 2: Forms.

Figure F.5. FileDocument Configuration for an MDI Child Form


Except for the code generated by the Designer, our complete MDI child form document management implementation looks like this:

// RatesOfReturnForm.cs (MDI Child Form) partial class RatesOfReturnForm : Form {    BindingList<PeriodReturn> periodReturns;    public RatesOfReturnForm() {      InitializeComponent();      periodReturns = new BindingList<PeriodReturn>();      this.PeriodReturnBindingSource.DataSource = this.periodReturns;    }    // For opening document from command line arguments    public bool OpenDocument(string filename) {      return this.fileDocument.Open(filename);    }    void fileDocument_NewDocument(object sender, EventArgs e) {      // Reset list      this.periodReturns.Clear();      this.periodReturns.Add(new PeriodReturn("start", 0M, 1000M));    }    void fileDocument_ReadDocument(      object sender, SerializeDocumentEventArgs e) {      // Deserialize object      IFormatter formatter == new BinaryFormatter();      this.periodReturns =        (BindingList<PeriodReturn>)formatter.Deserialize(e.Stream);      this.PeriodReturnBindingSource.DataSource = this.periodReturns;      // Calculate returns on reload      ...    }    void fileDocument_WriteDocument(      object sender, SerializeDocumentEventArgs e) {      // Serialize object      IFormatter formatter = new BinaryFormatter();      formatter.Serialize(e.Stream, this.periodReturns);    }    void PeriodReturnBindingSource_ListChanged(      object sender, ListChangedEventArgs e) {      // Recalculate returns      ...      // Update the dirty bit      this.fileDocument.Dirty = true;      ...    }    void CalculateReturns(int periods) {      // Calculate average and annual returns      ...    } }


The MDI parent form doesn't have an instance of the FileDocument but instead implements File | New and File | Open by creating new instances of the MDI child form, passing the file name from the command line if it gets one:

// MdiParentForm.cs partial class MdiParentForm : Form {    // For use by Main in processing a file passed via the command line    public void OpenDocument(string fileName) {      // Let child do the opening      RatesOfReturnForm child = new RatesOfReturnForm();      if( child.OpenDocument(fileName) ) {        child.MdiParent = this;        child.Show();      }      else {        child.Close();      }    }    void newToolStripMenuItem_Click(object sender, EventArgs e) {      // Create and show a new child      RatesOfReturnForm child = new RatesOfReturnForm();      child.MdiParent = this;      child.Show();    }    void openToolStripMenuItem_Click(object sender, EventArgs e) {      this.OpenDocument(null);    }    void exitToolStripMenuItem_Click(object sender, EventArgs e) {      // Children will decide whether or not this is allowed      this.Close();    } }


You implement File | Exit in the MDI parent by closing the form and letting the FileDocument component judge whether the MDI children can be closed based on the dirty bit and the user's input.

The results of hosting a FileDocument component in each MDI child and wiring it up (including the addition of the MDI features described in Chapter 2) are shown in Figure F.6.

Figure F.6. Using the FileDocument Component in an MDI Application





Windows Forms 2.0 Programming
Windows Forms 2.0 Programming (Microsoft .NET Development Series)
ISBN: 0321267966
EAN: 2147483647
Year: 2006
Pages: 216

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net