MDI-Anwendungen in C# - Jürgen Bayer
←
→
Transkription von Seiteninhalten
Wenn Ihr Browser die Seite nicht korrekt rendert, bitte, lesen Sie den Inhalt der Seite unten
Jürgen Bayer MDI-Anwendungen in C#
Inhaltsverzeichnis 1 Grundlagen 2 1.1 Einrichten der Formulare 2 1.2 Öffnen von MDI-Childformularen 3 2 Menüs 4 2.1 Erstellen eines Menüs 4 2.2 Programmierung der Menüpunkte für das Beispiel 6 2.3 Ein Fenster-Menüpunkt 10 3 Zugriff auf die Childs und den Parent 11 3.1 Zugriff auf MDI-Childs vom MDI-Parent aus 11 3.2 Zugriff auf dem MDI-Parent von MDI-Childs aus 12
MDI-Anwendungen starten mit einem Formular, dessen Eigenschaft IsMdiContainer auf true eingestellt ist. Diese Formulare werden dann als MDI-Parent bezeichnet. Wenn Sie ausgehend von einem solchen Formular andere Formulare öffnen, können Sie das Startformular nun über die Eigenschaft MDIParent als MDI-Parent des anderen Formulars angeben. Das andere Formular wird dann zum MDI-Child. MDI-Child- Formulare werden immer innerhalb des MDI-Parent angezeigt. Zusätzliche Features, wie das einfache Einrichten eines Fenster-Menüpunkts, der automatisch alle geöffneten MDI-Childs auflistet, das halbautomatische Ausrichten der MDI-Childs über die Methode LayoutMDI, und die Tatsache, dass das Menü eines MDI-Childs in das Menü des MDI-Parent eingeblendet wird, wenn das MDI-Child-Formular aktiv ist, erleichtern die Programmierung solcher Anwendungen. Microsoft Word ist übrigens ein gutes Beispiel für eine MDI-Anwendung: Das Hauptfenster von Word ist der MDI-Parent, die geöffneten Dokumente sind MDI- Childs. Für eine MDI-Anwendung benötigen Sie zunächst ein Startformular, dessen Eigenschaft IsMdiContainer auf true eingestellt ist. Das Formular sollte mit einem Menü ausgerüstet werden. Abbildung 1.1: Ein MDI-Parent-Formular mit Menü Ich entwickle in diesem Artikel eine einfache Textverarbeitung. Das MDI-Parentformular sollte dazu mit den Menüpunkten entsprechend Abbildung ausgerüstet werden. Die Menüpunkte Speichern und Schließen werden später Teil des MDI-Child-Formulars. MDI-Child-Formulare sind normale Formulare, die auch ein Menü besitzen können. Das Beispiel verwendet ein Formular mit einem RichTextBox-Steuerelement, in dem der Anwender einen RTF-Text bearbeiten kann. Grundlagen 2
Abbildung 1.2: Ein Formular mit RichTextBox und Menü als Basis für ein MDI-Child-Formular Das Format-Menü des Child-Formulars enthält im Beispiel die Menüpunkte Automatischer Zeilenumbruch und Schriftart. Bevor ich die Programmierung der Menüpunkte und des Konstruktors des Child-Formulars beschreibe, zeige ich, wie Sie dieses grundsätzlich öffnen und wie Sie das Menü so einstellen, dass dieses korrekt in das Menü des Parent-Formulars eingeblendet wird. Öffnen können Sie ein MDI-Child-Formular wie ein normales Formular. Vor dem Öffnen setzen Sie die Eigenschaft MDIParent des Formulars allerdings auf das MDI-Parent-Formular: private void mnuFileNew_Click(object sender, System.EventArgs e) { // Neues Editierformular erzeugen EditForm frmEdit = new EditForm(); // und unmodal als MDI-Child anzeigen frmEdit.MdiParent = this; frmEdit.Show(); } Grundlagen 3
Das Menü des Child-Formulars wird abhängig von den Eingeschaften MergeType und MergeOrder der einzelnen Menüeinträge in das Menü des Parent-Formulars eingeblendet. Für MergeType sind die in Tabelle 1 dargestellten Einstellungen möglich. Beachten Sie, dass Sie die Einstellung für die korrespondierenden Menüpunkte im Child- und im Parent-Formular vornehmen müssen. Wert Bedeutung Add Diese Einstellung gilt für einzelne Menüpunkte und bewirkt, dass der Menüpunkt dem Menü des MDI-Parentformulars hinzugefügt wird, auch wenn es bereits einen Menüpunkt mit derselben Beschriftung auf derselben Menüebene gibt. Bei gleich beschrifteten Menüpunkten auf einer Ebene existieren dann zwei Einträge. Replace Diese Einstellung gilt ebenfalls für einzelne Menüpunkte und bewirkt, dass der Menüpunkt des Childformulars einen vorhandenen gleich beschrifteten Menüpunkt des Parentformulars auf derselben Ebene ersetzt. MergeItems Diese Einstellung gilt für die Untermenüpunkte eines Menüpunkts und bewirkt, dass das Menü des Childformulars mit einem gleichnamigen Menü des Parentformulars zu einem einzelnen Menüpunkt zusammengefasst wird. Die Art der Zusammenfassung hängt von der Einstellung der untergeordneten Menüpunkte ab (Add, Replace oder Remove). Remove Diese Einstellung gilt wieder für einzelne Menüpunkte und bewirkt, dass der Menüpunkt entfernt wird, wenn zwei Menüs kombiniert werden. Sinnvoll ist diese Einstellung für Menüpunkte des Parentformulars, die nicht sichtbar sein sollen, wenn ein Childformular geöffnet ist. Tabelle 1: Die Einstellungen der MergeType-Eigenschaft für Menüpunkte Wenn die Untermenüpunkte zweier Menüs kombiniert werden, hängt die Position der Menüpunkte von der Eigenschaft MergeOrder ab. Menüs 4
Die Menüpunkte des Datei-Menüs des Textverarbeitungs-Beispiels sind folgendermaßen eingestellt, damit dieses Menü korrekt kombiniert wird: Formular Menüpunkt MergeType MergeOrder Parent &Datei MergeItems 0 Parent &Neu Add 0 Parent &Öffnen Add 1 Parent - Add 5 Parent &Beenden Add 6 Child &Datei MergeItems 0 Child - Add 2 Child &Speichern Add 3 Child S&chließen Add 4 Tabelle 2: Einstellungen der MergeType- und MergeOrder-Eigenschaft der Beispielanwendung Abbildung 2.1 zeigt das MDI-Parentformular ohne geöffnetes Childformular, in Abbildung 2.2 ist ein Childformular geöffnet (und damit das Datei-Menü des MDI-Parent mit dem Datei-Menü des MDI-Child kombiniert). Abbildung 2.1: Das MDI-Parentformular der Beispielanwendung ohne geöffnetes Childformular Menüs 5
Abbildung 2.2: Das MDI-Parentformular der Beispielanwendung mit geöffnetem Childformular Die Menüpunkte, die zum Childformular gehören, beziehen sich immer auf das gerade aktive Childformular. Die Programmierung der Menüpunkte ist damit sehr einfach. ! " # ! Der Neu-Menüpunkt des MDI-Parentformulars erzeugt ein neues MDI-Childformular. Der Öffnen-Menüpunkt erzeugt ein neues MDI-Childformular, öffnet einen Datei-Öffnen-Dialog und öffnet die vom Anwender ausgewählte Datei im RichTextBox-Steuerelement des MDI-Childformulars. Das Öffnen der Datei geschieht im Konstuktor des Childformulars, weswegen dieser ein Argument erhält, das definiert, ob ein neues Dokument erzeugt oder ein vorhandenes geöffnet werden soll. Zur Verwaltung der Dokumenteninformationen enthält die Klasse drei Eigenschaften: // Eigenschaften zur Verwaltung des Dokuments private string fileName = null; public string FileName { get {return fileName;} } private static int counter = 0; // Eigenschaft, die die Modified-Eigenschaft // des RichTextBox-Steuerelements veröffentlicht public bool Modified { get {return rtbDocument.Modified;} } Der Konstruktor sieht so aus: public EditForm(bool openExisting) { InitializeComponent(); if (openExisting) { // Vorhandenes Dokument öffnen. // Die eventuelle Ausnahme, die beim // Öffnen erzeugt wird, wird einfach // an den Aufrufer weitergegeben ofdRtfFile.InitialDirectory = Application.StartupPath; if (ofdRtfFile.ShowDialog() == DialogResult.OK) { // Datei laden Menüs 6
rtbDocument.LoadFile(ofdRtfFile.FileName, RichTextBoxStreamType.RichText); // Dokument als nicht modifiziert kennzeichnen rtbDocument.Modified = false; // Dateiname merken this.fileName = ofdRtfFile.FileName; // Beschriftung setzen System.IO.FileInfo fi = new System.IO.FileInfo(this.fileName); this.Text = fi.Name; } else { // Ausnahme werfen throw new CancelException(); } } else { // Neues Dokument counter++; this.Text = "Dokument " + counter; } } Das Argument openExisting steuert, ob eine vorhandene Datei geöffnet werden soll. Soll ein vorhandenes Dokument geöffnet werden (openExisting == true), öffnet das Programm einen Datei-Öffnen-Dialog (ofdRtfFile). Wenn der Anwender eine Datei ausgewählt hat, wird diese geöffnet. Ausnahmen, die dabei eventuell erzeugt werden, werden einfach an den Aufrufer weitergegeben. Betätigt der Anwender den Abbrechen-Schalter im Datei-Öffnen-Dialog, erzeugt das Programm einfach eine neue Ausnahme, deren Klasse in der Datei folgendermaßen implementiert ist: public class CancelException: Exception { } Im Konstruktor wird die Eigenschaft Modified des RichTextBox-Steuerelements auf false gesetzt. Über diese Eigenschaft ermittelt das Programm beim Schließen des Formulars, ob der Text im Steuerelement geändert wurde. Im MDI-Parent-Formular sieht der Quellcode des Neu- und des Öffnen-Menüpunkts folgendermaßen aus: private void mnuFileNew_Click(object sender, System.EventArgs e) { // Neues Editierformular erzeugen EditForm frmEdit = new EditForm(false); // und unmodal als MDI-Child anzeigen frmEdit.MdiParent = this; frmEdit.Show(); } private void mnuFileOpen_Click(object sender, System.EventArgs e) { // Neues Editierformular erzeugen bool ok = false; EditForm frmEdit = null; try { // Der Konstruktor ruft den Datei-Öffnen-Dialog // auf und erzeugt eine Ausnahme, wenn die Datei // nicht geöffnet werden kann oder wenn der // Anwender den Abbrechen-Schalter betätigt frmEdit = new EditForm(true); ok = true; } Menüs 7
catch (CancelException) { // Nichts machen } catch (Exception ex) { MessageBox.Show(ex.Message, Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Error); } if (ok) { // Wenn alles OK ist, Form unmodal // als MDI-Child anzeigen frmEdit.MdiParent = this; frmEdit.Show(); } } Der Speichern-Menüpunkt des MDI-Childformulars ruft die Methode SsaveFile auf: private void mnuFileSave_Click(object sender, System.EventArgs e) { // Dokument speichern this.saveFile(); } // Methode zum Speichern private bool saveFile() { // Dateiname ermitteln if (fileName == null) { // Datei-Speichern-Dialog öffnen sfdRtfFile.InitialDirectory = Application.StartupPath; if (sfdRtfFile.ShowDialog() == DialogResult.OK) { fileName = sfdRtfFile.FileName; } } // Datei speichern if (fileName != null) { try { // Datei speichern rtbDocument.SaveFile(fileName, RichTextBoxStreamType.RichText); // Dokument als nicht modifiziert kennzeichnen rtbDocument.Modified = false; // Beschriftung setzen System.IO.FileInfo fi = new System.IO.FileInfo(this.fileName); this.Text = fi.Name; return true; } catch (Exception ex) { MessageBox.Show(ex.Message, Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Error); return false; } } Menüs 8
else { return false; } } Der Schließen-Menüpunkt schließt das Formular (was auch sonst ...): private void mnuFileClose_Click(object sender, System.EventArgs e) { // Dokument schließen (die Abfrage, ob gespeichert // werden soll, erfolgt im Closing-Ereignis) this.Close(); } Im Closing-Ereignis des Formulars fragt das Programm ab, ob das Dokument geändert wurde und ruft gegebenenfalls die Save-Methode auf: private void EditForm_Closing(object sender, System.ComponentModel.CancelEventArgs e) { // Überprüfen, ob das Dokument geändert wurde if (rtbDocument.Modified) { // Abfragen, ob gespeichert werden soll switch (MessageBox.Show("Möchten Sie Ihre " + "Änderungen an '" + this.Text + "' speichern?", Application.ProductName, MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question)) { case DialogResult.Yes: if (this.saveFile() == false) { // Wenn nicht gespeichert werden kann, // wird das Schließen abgebrochen e.Cancel = true; } break; case DialogResult.Cancel: // Abbrechen e.Cancel = true; break; } } } Nun fehlen zunächst noch die Ereignisbehandlungsmethoden für die Menüpunkte des Format-Menüs. Die Methode für die Schriftart nutzt einen Schriftauswahldialog (fdRtfFont): private void mnuFormatFont_Click(object sender, System.EventArgs e) { // Schriftart der aktuellen Selektion definieren if (fdRtfFont.ShowDialog() == DialogResult.OK) { rtbDocument.SelectionFont = fdRtfFont.Font; rtbDocument.SelectionColor = fdRtfFont.Color; } } Menüs 9
Die Methode für den automatischen Zeilenumbruch schaltet die Eigenschaft WordWrap des RichTextBox- Steuerelements um: private void mnuFormatWordWrap_Click(object sender, System.EventArgs e) { // WordWrap umschalten mnuFormatWordWrap.Checked = !mnuFormatWordWrap.Checked; rtbDocument.WordWrap = mnuFormatWordWrap.Checked; } $ ! " Das MDI-Parent-Formular besitzt normalerweise ein Menü Fenster, über den Sie die einzelnen geöffneten Dokumente erreichen und ausrichten können. Eine Liste der geöffneten Dokumente erhalten Sie automatisch, wenn Sie die Eigenschaft MdiList dieses Menüs auf true setzen. Für die üblichen Menüpunkte zum Ausrichten der Fenster nutzen Sie die LayoutMdi-Methode: private void mnuWindowCascade_Click(object sender, System.EventArgs e) { // Fenster kaskadierend anordnen this.LayoutMdi(MdiLayout.Cascade); } private void mnuWindowTileVertical_Click(object sender, System.EventArgs e) { // Fenster vertikal geteilt anordnen this.LayoutMdi(MdiLayout.TileVertical); } private void mnuWindowTileHorizontal_Click(object sender, System.EventArgs e) { // Fenster horizontal geteilt anordnen this.LayoutMdi(MdiLayout.TileHorizontal); } private void mnuWindowArrangeIcons_Click(object sender, System.EventArgs e) { // Fenstersymbole anordnen this.LayoutMdi(MdiLayout.ArrangeIcons); } Menüs 10
$% $ % Wenn Sie vom MDI-Parent aus auf MDI-Childs zugreifen wollen, können Sie die Eigenschaft ActiveMdiChild verwenden, die eine Referenz auf das gerade aktive MDI-Childformular darstellt. Im Ereignis MdiChildActivate können Sie darauf reagieren, dass ein MDI-Childformular aktiviert wurde. Das folgende Beispiel schreibt den Dateinamen des aktiven Dokuments und eine Information darüber, ob das Dokument ungespeicherte Änderungen enthält, in ein StatusBar-Steuerelement auf dem MDI-Parent-Formular: // Methode zum Setzen des Textes der StatusBar public void SetStaturBarText() { if (ActiveMdiChild != null) { sbr.Text = ((EditForm)ActiveMdiChild).FileName; if (((EditForm)ActiveMdiChild).Modified) { sbr.Text += " (geändert)"; } } else { sbr.Text = ""; } } private void StartForm_MdiChildActivate(object sender, System.EventArgs e) { // Wenn ein MDI-Child-Formular aktiviert wird: // Den Dateinamen des Dokuments und eine Information // darüber, ob die letzten Änderungen am Text noch // nicht gespeichert sind, in der StatusBar ausgeben this.SetStaturBarText(); } Zugriff auf die Childs und den Parent 11
$ % Ein MDI-Childformular kann über seine Eigenschaft MdiParent auch auf das MDI-Parentformular zugreifen. Das folgende Beispiel erweitert die Speichern-Methode so, dass beim Speichern die SetStaturBarText- Methode des MDI-Parent aufgerufen wird: bool saveFile() { // Dateiname ermitteln if (fileName == null) { // Datei-Speichern-Dialog öffnen sfdRtfFile.InitialDirectory = Application.StartupPath; if (sfdRtfFile.ShowDialog() == DialogResult.OK) { fileName = sfdRtfFile.FileName; } } // Datei speichern if (fileName != null) { try { // Datei speichern rtbDocument.SaveFile(fileName, RichTextBoxStreamType.RichText); // Dokument als nicht modifiziert kennzeichnen rtbDocument.Modified = false; // Beschriftung setzen System.IO.FileInfo fi = new System.IO.FileInfo(this.fileName); this.Text = fi.Name; // StatusBar-Text des MDI-Parent neu definieren ((StartForm)this.MdiParent).SetStaturBarText(); return true; } catch (Exception ex) { MessageBox.Show(ex.Message, Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Error); return false; } } else { return false; } } Zugriff auf die Childs und den Parent 12
Sie können auch lesen