Erstellen Document-Centric Anwendungen in Windows Forms, Teil 1

 

Chris Sells
Microsoft Corporation

2. September 2003

Zusammenfassung: Chris Sells beschreibt eine Implementierung der einfachen Dokumentverarbeitung für eine SDI-Anwendung, einschließlich ordnungsgemäßer Speicherfunktionen, aktualisierter Beschriftungen basierend auf Dem Dateinamen und Zustand und öffnen von Dokumenten, die über die Befehlszeile übergeben werden. (14 gedruckte Seiten)

Laden Sie die wfdocs1src.msi Beispieldatei herunter.

Das ist es, was mir immer passiert – ich lese etwas und ich zweifele daran, was es sagt, also muss ich ein Computerprogramm schreiben, um selbst zu sehen. Dieses Mal zweifelte ich an der angegebenen annualisierten Rendite für einen Investmentfonds bei einer Reihe von jährlichen Renditen. Meine Lesart war, dass der Autor behauptete, dass die Zahlen eine annualisierte Rendite von 24,91% darstellten, die ich überprüfen musste, nachdem ich gerade den Unterschied zwischen der durchschnittlichen und der annualisierten Rendite ermittelt hatte (unterm Strich können Sie annualisierte Renditen ausgeben, während die durchschnittlichen Renditen wertlos sind). In diesem Sinne habe ich die RateOfReturn-Anwendung in Abbildung 1 (mit den Zahlen des Autors aufgefüllt) erstellt.

Abbildung 1. Die RateOfReturn-Anwendung in Aktion

Leider war es erst, nachdem ich die Anwendung erstellt hatte, erkannte ich, dass die Zahlen, die der Autor gemeldet hat, keine tatsächlichen Raten waren, sondern Deltas vom S&P 500 für die gleichen Zeiträume, aber das lässt mich nur dumm aussehen, also konzentrieren wir uns auf andere Dinge.

Document-Based Anwendungen

Der Großteil der Anwendung in Abbildung 1 wurde mit den Visual Studio® .NET-Designern erstellt, einschließlich des Datenrasters, einer Datenansicht, um das ListChanged-Ereignis verfügbar zu machen, wenn sich das Dataset geändert hat, ein typisiertes Dataset, das die Datenstruktur meiner Anwendung darstellt, und die Bindung zwischen ihnen. Sogar die Formatierung der Spalten für Prozentsätze und Währungen wurde mithilfe des Tabellenformat-Editors für das Datenraster definiert. Mit Ausnahme der dokumentbezogenen Menüelemente (z. B. File-Open>) musste ich nur den Form.Load-Ereignishandler schreiben, um das Dataset mit einer ersten Zeile und dem DataView.ListChanged-Ereignishandler vorab aufzufüllen, um die Durchschnitts- und Jahresraten von Rückgabeberechnungen zu implementieren:

void Form1_Load(object sender, System.EventArgs e) {
  // Add starting principle
  this.periodReturnsSet1.PeriodReturn.AddPeriodReturnRow(
    "start", 0M, 1000M);
}

void dataView1_ListChanged(object sender, ListChangedEventArgs e) {
  // Calculate average and annual returns
  ...
}

Ich habe meine deklarative Entwicklungserfahrung sehr gerne genutzt, bis ich mein neu aufgefülltes Dataset zur späteren Verwendung auf dem Datenträger speichern wollte. Während Windows Forms und Visual Studio .NET alle Arten von Unterstützung bieten, um meine datengebundene Anwendung einfach zu schreiben, bot es keine echte Unterstützung für die Grundnahrungsmittel von MFC -Programmierern (Microsoft Foundation-Klassen) überall – dokumentbasierte Anwendungen. MFC ist so in der Lage, dokumentbasierte Anwendungen zu unterstützen, dass häufig Anwendungen, die nicht dokumentbasiert waren, so werden, um in das MFC-Modell zu passen (obwohl MFC sicherlich flexibel genug ist, um viele verschiedene Arten von Anwendungen zu verarbeiten).

Windows Forms hingegen, während sie datenorientierte Anwendungen erstellen können, bieten fast keine Unterstützung für dokumentbasierte Anwendungen. Oh, es war einfach genug für mich, das Menü Datei zu layouten und die Dateidialoge anzuzeigen. Es war sogar einfach für mich, den Inhalt des Datasets mithilfe des Serialisierungsstapels in .NET auf den Datenträger zu speichern:

using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters;
using System.Runtime.Serialization.Formatters.Soap;
...
void fileSaveMenuItem_Click(object sender, EventArgs e) {
  if( this.saveFileDialog1.ShowDialog(this) != DialogResult.OK ) {
    return;
  }

  string fileName = this.saveFileDialog1.FileName;
  using( Stream stream = new FileStream(
           fileName, FileMode.Create, FileAccess.Write) ) {
    // Serialize object to text format
    IFormatter formatter = new SoapFormatter();
    formatter.Serialize(stream, this.periodReturnsSet1);
  }
}

All diese Funktionen sind in .NET integriert. Einige davon befinden sich im System.Windows.Forms-Namespace , einige in System.IO und einige in System.Runtime.Serialization, aber es ist alles für Ihren zugriff verfügbar.

Dokumentbasierte Anwendungen erfordern jedoch viel mehr als nur das Anzeigen eines Dateidialogfelds und das Ausladen des Inhalts eines Objekts in eine Datei. Um die grundlegenden Erwartungen eines Microsoft Windows-Benutzers® zu erfüllen, benötigt selbst eine minimale SDI-Anwendung (Single Document Interface) die folgenden Features:

  • Zeigt das aktuell geladene Dokument im Untertitel des Formulars an (z. B. Untitled.txt).
  • Zeigt dem Benutzer an, dass sich ein Dokument auf dem Datenträger geändert hat, in der Regel mit einem Sternchen neben dem Dateinamen (Untitled.txt*).
  • Fordert den Benutzer auf, ein geändertes Dokument zu speichern, wenn er versucht, es ohne Speichern zu schließen.
  • Ermöglicht es dem Benutzer, Änderungen am aktuellen Dokument zu speichern, ohne den Dateinamen für jede Änderung bereitzustellen. Beispielsweise der Unterschied zwischen Datei-Speichern> nach dem ersten Speichern und Datei-Speichern> unter.
  • Erstellt neue Dokumente und löscht alle aktuell aktiven Dokumente.
  • Öffnet zuvor gespeicherte Dateien.

Auf Programmiererebene können diese Features mit den folgenden Konstrukten implementiert werden:

  • Ereignisse, die ausgelöst werden, wenn das Dokument geändert wurde.
  • Ein modifiziert Bit, um den Zustand des Dokuments relativ zur Kopie auf dem Datenträger nachzuverfolgen, um die Untertitel aufzufüllen und den Benutzer bei Bedarf zum Speichern aufzufordern.
  • Ein aktueller Dateiname zum Auffüllen des Untertitel und Implementieren von Datei-Speichern> nach einem anfänglichen Speichern.
  • Handler für die verschiedenen Ereignisse im Zusammenhang mit dem Menü Datei (File-New>, File-Save> usw.).

Während Windows Forms kein Feature bietet, das diese Konstrukte zusammenfügt, können sie relativ einfach zu unserem SDI-Beispiel hinzugefügt werden.

Nachverfolgen des schmutzigen Bits

Das Feststellen, wann sich das Dokument geändert hat, ist anwendungsspezifisch. In unserem Beispiel werden die Änderungen der Dokumentdaten durch die DataView angegeben. ListChanged-Ereignis :

// Tracking the dirty bit
bool dirty = false;

void SetDirty(bool dirty) {
  this.dirty = dirty;
  SetCaption();
}

// Set caption based on application name, file name and dirty bit
void SetCaption() {...}

void dataView1_ListChanged(object sender, ListChangedEventArgs e) {
  ...
  // Update the dirty bit
  SetDirty(true);
}

Beachten Sie, dass beim Auslösen des ListChanged-EreignissesSetDirty aufgerufen wird, anstatt das Feld modifiziert direkt festzulegen. Dies empfiehlt sich, da SetDirty auch die neuen Untertitel mithilfe des aktualisierten Felds modifiziert festlegt. Wenn Sie das modifiziert Feld vergessen und direkt festgelegt haben, müssen Sie auch daran denken, die SetCaption-Methode aufzurufen.

Nachverfolgen des aktuellen Dateinamens

Wie beim modifiziert Bit ändert sich der aktuell aktive Dateiname während der Lebensdauer einer dokumentbasierten Anwendung. Meistens ändert sich der Dateiname während der Implementierung der Menüelemente Datei , aber die grundlegende Unterstützung für einen aktuellen Dateinamen kann wie folgt implementiert werden:

// Tracking the current file name
string fileName = null;

void SetFileName(string fileName) {
  this.fileName = fileName;
  SetCaption();
}

static bool Empty(string s) { return s == null || s.Length == 0; }

// Set caption based on application name, file name and dirty bit
void SetCaption() {
  this.Text = string.Format(
    "{0} - [{1}{2}]",
    Application.ProductName,
    Empty(this.fileName) ?
      "Untitled.ror" :
      Path.GetFileName(this.fileName),
    this.dirty ? "*" : "");
}

Nebenbei können Sie sich fragen, warum ich die Untertitel als formatiert habe:

Application – [FileName*]

Anstelle des Editor/Office-Standards von:

FileName* – Application

Ich habe mich für erstere entschieden, um mit der Implementierung Windows Forms Multiple Document Interface (MDI) konsistent zu sein, die auch den früheren Stil verwendet, im Gegensatz zu Editor und Microsoft Office, die den letzteren Stil verwendet. In Teil 2 dieser Reihe werden dokumentorientierte MDI-Anwendungen behandelt.

Beachten Sie schließlich, dass der Dateiname mit NULL statt mit "Untitled.ror" beginnt. Null ist ein Signal an die Save-Methodenfamilie , dass der Benutzer noch keinen Dateinamen ausgewählt hat.

Datei speichern> und Freunde

Die Implementierung von File-Save>, Save Unter und Save Copy As ist der komplizierteste Teil dieses gesamten Prozesses, da es sich um drei leicht unterschiedliche Möglichkeiten handelt, um den Inhalt Ihrer Dokumentdaten auf den Datenträger zu speichern. Im Folgenden werden das verhalten aufgeführt, das von Windows-Anwendungen erwartet wird (und das Verhalten, das MFC aufweist):

  • Save weist die folgenden Verhaltensweisen auf:
    • Zeigt das Dialogfeld Datei speichern an, wenn kein aktueller Dateiname vorhanden ist.
    • Serialisiert die Dokumentdaten auf dem Datenträger.
    • Löscht das modifiziert Bit.
    • Legt den aktuellen Dateinamen fest, falls noch keiner vorhanden ist.
    • Updates das Formular Untertitel mit dem neuen Status des modifiziert Bits und möglicherweise einem neuen Dateinamen.
  • Speichern unter weist die folgenden Verhaltensweisen auf:
    • Zeigt immer das Dialogfeld "Datei speichern" an.
    • Serialisiert die Dokumentdaten auf dem Datenträger.
    • Löscht das modifiziert Bit.
    • Legt den aktuellen Dateinamen auf den neuen Dateinamen fest.
    • Updates das Formular Untertitel.
  • Save Copy Unter weist die folgenden Verhaltensweisen auf:
    • Zeigt immer das Dialogfeld "Datei speichern" an.
    • Serialisiert die Dokumentdaten auf dem Datenträger.
    • Löscht das modifiziert Bit nicht.
    • Legt den aktuellen Dateinamen nicht auf den neuen Dateinamen fest.
    • Aktualisiert das Formular nicht Untertitel.

Der folgende Code zeigt eine Möglichkeit, diese Verhaltenssätze zu implementieren:

protected enum SaveType {
  Save,
  SaveAs,
  SaveCopyAs,
}

bool SaveDocument(SaveType type) {
  // Get the file name
  string newFileName = this.fileName;
  if( (type == SaveType.SaveAs) ||
    (type == SaveType.SaveCopyAs) ||
    Empty(newFileName) ) {

    if( !Empty(newFileName) ) {
      saveFileDialog1.InitialDirectory =
        Path.GetDirectoryName(newFileName);
      saveFileDialog1.FileName =
        Path.GetFileName(newFileName);
    }
    else {
      saveFileDialog1.FileName = "Untitled.ror";
    }

    DialogResult res = saveFileDialog1.ShowDialog(this);
    if( res != DialogResult.OK ) return false;
    newFileName = saveFileDialog1.FileName;
  }

  // Write the data
  try {
    using( Stream stream = new FileStream(
              newFileName, FileMode.Create, FileAccess.Write) ) {
      // Serialize object to text format
      IFormatter formatter = new SoapFormatter();
      formatter.Serialize(stream, this.periodReturnsSet1);
    }
  }
  catch( Exception e ) {
    // report error...
    return false;
  }

  if( type != SaveType.SaveCopyAs ) {
    // Clear the dirty bit, set the current file name
    // and the caption is set automatically
    SetDirty(false);
    SetFileName(newFileName);
  }

  // Success
  return true;
}

Beachten Sie, dass die SaveDocument-Methode ein Argument verwendet, das angibt, welche Art von Speicher wir ausführen (d. a. Speichern, Speichern unter oder Kopieren speichern unter). Speichern muss nur nach einem neuen Dateinamen fragen, wenn es noch keinen gibt, während "Speichern unter" und "Kopieren unter speichern" jedes Mal gefragt werden müssen. Speichern und Speichern unter löschen Sie das modifiziert Bit, und legen Sie den neuen Dateinamen auf aktuell fest, aber Kopieren unter speichern tut dies nicht. Alle drei Speichern schreiben jedoch die Dokumentdaten in die Datei ihrer Wahl.

Wenn die Save-Methode mit mehreren Modus vorhanden ist, ist die Implementierung der drei Menüoptionen einfach:

void fileSaveMenuItem_Click(object sender, EventArgs e) {
  SaveDocument(SaveType.Save);
}

void fileSaveAsMenuItem_Click(object sender, EventArgs e) {
  SaveDocument(SaveType.SaveAs);
}

void fileSaveCopyAsMenuItem_Click(object sender, EventArgs e) {
  SaveDocument(SaveType.SaveCopyAs);
}

Dateineu>

Eine Sache, die ich nicht Erwähnung, war, warum die SaveDocument-Methode einen booleschen Wert zurückgibt, obwohl die Klickhandler des Menüelements es nicht überprüfen. Das Ergebnis eines Speichervorgangs ist wichtig, wenn er Teil eines Vorgangs ist, der die aktuellen Daten entladen und durch neue Daten ersetzen muss, wie file-New>.

New weist die folgenden Verhaltensweisen auf:

  • Wenn die Dokumentdaten modifiziert sind, wird der Benutzer aufgefordert, seine Änderungen zu speichern, die Änderungen abzubrechen oder den Vorgang Neu abzubrechen.
  • Wenn der Benutzer speichern möchte, ruft er Save(Save) auf und fordert ggf. einen Dateinamen ein. Wenn die Save-Methode fehlschlägt, wird der New-Vorgang abgebrochen, wobei die Änderungen des Benutzers beibehalten werden. Dies geschieht, wenn der Datenträger voll ist oder der Benutzer im Dateidialogfeld auf Abbrechen gedrückt hat.
  • Wenn der Benutzer den Vorgang Neu abbrechen möchte, wird nichts gespeichert, und es wird kein neues Dokument erstellt.
  • Wenn das modifiziert Bit klar ist oder der Benutzer seine Änderungen nicht speichern möchte oder er seine Änderungen gespeichert hat, muss das Dokument auf den Zustand "Neu" zurückgesetzt werden, genau wie beim Erstellen.

Wenn das modifiziert Bit festgelegt wird, wenn der Benutzer Datei-Neu> auswählt, wird ein Meldungsfeld wie Abbildung 2 angezeigt, um die Wahl zu erhalten, was mit den Änderungen an den Daten des Dokuments geschehen soll.

Abbildung 2. Auffordern des Benutzers, was mit seinen Dokumentänderungen zu tun ist

Zur Unterstützung von File-Open>, auf das ich weiter unten eingehen werde, lässt sich das Verhalten von New am einfachsten in zwei Methoden unterteilen: NewDocument und CloseDocument:

public bool NewDocument() {
  // Check to see if we can close
  if( !CloseDocument() ) return false;

  // Clear existing data
  ...

  // Set initial document data and state
  this.periodReturnsSet1.PeriodReturn.AddPeriodReturnRow(
    "start", 0M, 1000M);
  SetDirty(false);
  SetFileName(null);

  return true;
}

bool CloseDocument() {
  // It's all about the dirty bit...
  if( !this.dirty ) return true;

  DialogResult res = MessageBox.Show(
    this,
    "Save changes?",
    Application.ProductName,
    MessageBoxButtons.YesNoCancel);

  switch( res ) {
    case DialogResult.Yes: return SaveDocument(SaveType.Save);
    case DialogResult.No: return true;
    case DialogResult.Cancel: return false;
    default: Debug.Assert(false); return false;
  }
}

Beachten Sie, dass die CloseDocument-Methode das modifiziert Bit überprüft und einen Erfolg meldet, wenn es bereits klar ist, was angibt, dass keine Datenänderungen gespeichert werden müssen. Wenn Änderungen zu speichern sind, fordern wir den Benutzer mit seinen drei Auswahlmöglichkeiten auf, wobei der Erfolg oder Fehler der SaveDocument-Methode zugrunde liegt, sofern verwendet, um festzustellen, ob das Dokument erfolgreich gespeichert werden kann oder nicht.

Wenn das Dokument geschlossen werden kann, löscht die NewDocument-Methode die alten Daten, legt alle anfänglich neuen Daten (z. B. unsere Seedzeile) fest, löscht das modifiziert Bit und den aktuellen Dateinamen und aktualisiert die Untertitel für den Benutzer.

Wenn newDocument eingerichtet ist, können wir es verwenden, um sowohl den Konstruktor des Standard Formulars als auch das Menüelement Datei-Neu> zu implementieren:

public RatesOfReturnForm() {
  // Required for Windows Form Designer support
  InitializeComponent();

  // Create the new document
  // NOTE: Can't do this in Load, or opening documents
  // from the command line becomes difficult
  NewDocument();
}

void fileNewMenuItem_Click(object sender, EventArgs e) {
  NewDocument();
}

Datei öffnen>

Open weist die folgenden Verhaltensweisen auf:

  • Überprüft, ob das Datendokument geschlossen werden kann, und fordert den Benutzer auf, zu speichern, wenn die Daten modifiziert sind.
  • Wenn das Dokument geschlossen werden kann, wird das Dialogfeld "Datei geöffnet" angezeigt, und der Benutzer kann eine zu öffnende Datei auswählen.
  • Deserialisiert die ausgewählte Datei.
  • Löscht das modifiziert Bit.
  • Legt den aktuellen Dateinamen fest.
  • Updates die Untertitel.

Open ist im Wesentlichen eine Kombination aus dem Verhalten von Neu, das überprüft, ob die aktuellen Daten geschlossen und gespeichert werden können, interagiert mit dem Benutzer, um eine Datei auszuwählen und die Untertitel entsprechend zu aktualisieren. OpenDocument implementiert dieses Verhalten:

public bool OpenDocument(string newFileName) {
  // Check if we can close current file
  if( !CloseDocument() ) return false;

  // Get the file to open
  if( Empty(newFileName) ) {
    DialogResult res = openFileDialog1.ShowDialog(this);
    if( res != DialogResult.OK ) return false;
    newFileName = openFileDialog1.FileName;
  }

  // Read the data
  try {
    using( Stream stream = new FileStream(
              newFileName, FileMode.Open, FileAccess.Read) ) {
      // Deserialize object from text format
      IFormatter formatter = new SoapFormatter();
      PeriodReturnsSet
        ds = (PeriodReturnsSet)formatter.Deserialize(stream);

      // Clear existing data
      ...

      // Merge in new data, keeping data bindings intact
      ...
    }
  }
  catch( Exception e ) {
    // report error...
    return false;
  }

  // Clear dirty bit, set the current the file name
  // and set the caption
  SetDirty(false);
  SetFileName(newFileName);

  // Success
  return true;
}

Die Implementierung von File-Open> ist so einfach wie das Aufrufen der OpenDocument-Methode :

void fileOpenMenuItem_Click(object sender, EventArgs e) {
  OpenDocument(null);
}

Beachten Sie, dass die OpenDocument-Funktion ein Filename-Argument als optionalen Parameter akzeptiert und das Dialogfeld überspringt, wenn das Argument NULL ist. Beachten Sie auch, dass ich OpenDocument im Gegensatz zu den anderen Methoden, die ich bisher gezeigt habe, öffentlich gemacht habe. Beide Features ermöglichen das Öffnen eines Dokuments mithilfe einer optionalen Datei, die über die Befehlszeile übergeben wird.

Öffnen mithilfe der Befehlszeile

Alle .NET-Anwendungen, unabhängig davon, ob es sich um Konsolenanwendungen oder Windows-Anwendungen handelt, bieten den gleichen Zugriff auf die optionalen Befehlszeilenargumente, die beim ersten Start der Anwendung übergeben werden. Diese Argumente sind über das Zeichenfolgenarray verfügbar, das an die Main-Methode Ihrer Anwendung übergeben wird. Standardmäßig sieht der vom Assistenten generierte Code wie folgt aus, wobei die Befehlszeilenargumente ignoriert werden:

static void Main() {
  Application.Run(new Form1());
}

Die Behandlung der Befehlszeilenargumente mit OpenDocument an Ort und Stelle sieht wie folgt aus:

using System.IO;
...
static void Main(string[] args) {
  // 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);
}

Beachten Sie die Verwendung der Path.GetFullPath-Methode aus dem System.IO Namespace, um einen möglicherweise relativen Dateinamen in einen vollständigen Pfadnamen umzuwandeln. Dies ist nützlich, um sicherzustellen, dass wir immer vollständige Pfadnamen verwenden, genau wie die Dateidialoge.

Natürlich ist die Befehlszeilenverarbeitung am nützlichsten, wenn die Shell über Ihre benutzerdefinierte Dateierweiterung informiert wurde, sodass das Doppelklicken auf die Dateien Ihrer Anwendung im Explorer dazu führen, dass Ihre Anwendung gestartet wird und die Datei als Argument übergeben wird. Das Abrufen der Shell dazu für Ihre Windows Forms Anwendung wird in Teil 2 dieser Reihe behandelt.

Schließen und Beenden von> Dateien

Wenn der Benutzer Änderungen an dem Dokument vornimmt und auf die Schaltfläche Schließen im Formular (das X in der oberen rechten Ecke) klickt oder die Anwendung beendet, möchten wir sicherstellen, dass seine Daten gespeichert werden.

Schließen und Beenden weisen die folgenden Verhaltensweisen auf:

  • Überprüft, ob das Datendokument geschlossen werden kann, und fordert den Benutzer auf, zu speichern, wenn die Daten modifiziert sind.
  • Schließen oder vorhanden, wenn das Dokument geschlossen werden kann, andernfalls wird der Vorgang abgebrochen.

Die CloseDocument-Methode , die wir bereits für NewDocument und OpenDocument erstellt haben, reicht aus, um das Closing-Ereignis des Formulars zu implementieren (wenn es noch nicht zu spät ist, das Schließen abzubrechen):

void RatesOfReturnForm_Closing(object sender, CancelEventArgs e) {
  if( !CloseDocument() ) e.Cancel = true;
}

File-Exit kann implementiert werden, indem nur das Formular geschlossen wird und der Closing-Ereignishandler> entscheiden lässt, ob es in Ordnung ist, das Formular zu schließen oder nicht:

void fileExitMenuItem_Click(object sender, EventArgs e) {
  this.Close();
}

Wo sind wir

Trotz der Tatsache, dass ich die Zahlen aus dem Buch, das ich gelesen habe, falsch interpretiert habe, haben wir letztendlich eine Implementierung der einfachen Dokumentverarbeitung für eine SDI-Anwendung, einschließlich der richtigen New-, SaveAs-, SaveCopyAs-, Open-, Close- und Exit-Funktionalität, sowie die Untertitel des Formulars auf dem neuesten Stand gehalten, basierend auf dem Dateinamen des aktuellen Dokuments und modifiziert Zustand und sogar öffnende Dokumente, die über die Befehlszeile übergeben wurden.

In Teil zwei dieser Reihe erfahren Sie, was zu tun ist, um dieses Modell für MDI-Anwendungen zu erweitern, über eine tiefere Ebene der Shellintegration wie benutzerdefinierte Dateierweiterungen und das Hinzufügen von Dateien zum Menü Dokumente auf der Schaltfläche Start . Schließlich zeige ich die Vorteile der Übertragung all dieser generischen Dokumentverarbeitungsfunktionen in eine wiederverwendbare Komponente, sodass die meisten Arbeiten von der Entwurfsoberfläche aus erledigt werden können, wie von Alan Cooper beabsichtigt.

Referenzen

Chris Sells ist Content Strategist für MSDN Online und konzentriert sich derzeit auf Longhorn, das nächste Betriebssystem von Microsoft. Er hat mehrere Bücher geschrieben, darunter Mastering Visual Studio .NET und Windows Forms für C#-Programmierer. In seiner Freizeit veranstaltet Chris verschiedene Konferenzen, leitet das Genghis Source-Available-Projekt, spielt mit Rotor und macht im Allgemeinen einen Schädling von sich selbst in der Blogsphäre. Weitere Informationen zu Chris und seinen verschiedenen Projekten finden Sie unter http://www.sellsbrothers.com.