Rubrik: Forms/Controls | VB-Versionen: VB5, VB6 | 01.07.04 |
MDI-Projekt und das Verwalten von MDIChild-Formen In diesem Workshop erfahren Sie, wie man MDIChild-Formen in einer MDI-Anwendung gekonnt verwalten kann und jederzeit Zugriff auf eine ganz bestimmtes ChildForm hat. Als Beispiel für diesen Workshop soll eine WebBrowser-Anwendung mit beleibig vielen Browser-Fenstern erstellt werden. | ||
Autor: Dieter Otter | Bewertung: | Views: 41.335 |
In diesem Workshop erfahren Sie, wie man MDIChild-Formen in einer MDI-Anwendung gekonnt verwalten kann und jederzeit Zugriff auf eine ganz bestimmtes ChildForm hat. Als Beispiel für diesen Workshop soll eine WebBrowser-Anwendung mit beleibig vielen Browser-Fenstern erstellt werden.
MDI-Projekte werden immer dann besonders gern verwendet, wenn man mehrere "gleiche" Child-Windows innerhalb eines Hauptfensters anzeigen möchte. Ein klassisches Beispiel hierfür wäre eine eigene Browser-Anwendung mit der Möglichkeit mehrere Browser-Fenster gleichzeitig innerhalb der Haupt-Anwendung zu öffnen. Das ist eigentlich auch alles überhaupt kein Problem, wie nachfolgender 2-Zeiler verdeutlicht:
' Neues Browser-Fenster öffnen und anzeigen Dim oForm As New frmBrowser oForm.Show
Probleme gibt es erst dann, wenn man bspw. prüfen möchte, ob überhaupt ein Browser-Fenster (ChildForm) geöffnet ist. Ein beliebter Code hierfür ist das "Durchlaufen" aller Forms und gleichzeitiger Prüfung, ob es sich um das gesuchte Child-Form handelt:
Dim oForm As Form For Each oForm In VB.Forms If Not TypeName(oForm) = "MDIForm" Then If oForm.Name = "frmBrowser" Then ' Es gibt mind. 1 geöffnetes Browser-Fenster ' ... End If End If Next
Ok... mit diesem Code könnte man zunächst "leben". Was aber, wenn man alle geöffneten Browser-Fenster bspw. in einer ComboBox in der Haupt-Form anzeigen möchte? Natürlich sollte dann auch immer das aktive Browser-Fenster in der ComboBox markiert sein. Und wird ein Browser-Fenster geschlossen, muss dieses natürlich auch aus der ComboBox entfernt werden. Weiterhin sollte beim Klick auf einen ComboBox-Eintrag das dazugehörige Child-Fenster aktiviert und in den Vordergrund geholt werden.
Das alles lässt sich mit einem Collection-Objekt mit relativ wenig Aufwand verwalten! Wie? Das erfahren Sie auf den nachfolgenden Seiten.
Aufbau unseres MDI-Browsers
Starten Sie ein neues Projekt, benennen Form1 in frmBrowser um und setzen die Eigenschaft MDIChild auf den Wert True. Platzieren Sie auf die Form eine TextBox (txtURL) für die Eingabe einer Internetadresse und zusätzlich noch ein WebBrowser-Control (WebBrowser1). Die Anordnung spielt hierbei keine Rolle - das erledigen wir zur Laufzeit mit nachfolgendem Code:
Private Sub Form_Resize() ' Controls anordnen If Me.WindowState <> vbMinimized Then txtURL.Move 105, 105, Me.ScaleWidth - 210 With WebBrowser1 .Move 105, txtURL.Top + txtURL.Height + 105, Me.ScaleWidth - 210 .Height = Me.ScaleHeight - .Top - 105 End With End If End Sub
Wird eine URL in der TextBox angegeben soll die Webseite im WebBrowser-Control angezeigt werden:
Private Sub txtURL_GotFocus() ' Bei Focuserhalt, Inhalt der TextBox markieren txtURL.SelStart = 0 txtURL.SelLength = 999 End Sub
Private Sub txtURL_KeyPress(KeyAscii As Integer) ' Seite beim Drücken von ENTER im WebBrowser anzeigen If KeyAscii = 13 Then KeyAscii = 0 WebBrowser1.Navigate txtURL.Text End If End Sub
Abb 1.: frmBrowser (MDIChild)
Die MDI-Hauptanwendung: MDIForm1
Fügen Sie dem Projekt ein MDI-Formular hinzu (MDIForm1) - unser Hauptfenster. Auf der MDIForm benötigen wir lediglich eine PictureBox mit nachfolgenden Eigenschaften, sowie eine ComboBox (Combo1) und einen CommandButton (cmdNew).
Align | 1 - oben ausrichten |
BorderStyle | 0 - kein Rahmen |
Height | 435 |
TabStop | False |
Die ComboBox, in der später alle geöffneten Browser-Fenster aufgelistet werden sollen, platzieren Sie bitte auf die PictureBox, wie nachfolgende Abbildung zeigt und setzen die Style-Eigenschaft auf "2 - Dropdown-Liste". Den CommandButton platzieren Sie dann rechts neben die ComboBox.
Abb. 2: Das Haupt-Formular: MDIForm1
Das Collection-Objekt zur Verwaltung der Child-Formen
Kommen wir jetzt endlich zum Dreh- und Angelpunkt dieses Workshops: das Collection-Objekt.
Ein Collection-Objekt kann man sich als "Sammelbehälter" für die verschiedensten Datentypen und Objekte vorstellen. Über die AddItem-Methode fügt man dem "Behälter" ein neues Objekt hinzu, wobei man diesem sogar einen eindeutigen Key (Schlüsselwert) zuweisen kann. Mittels der RemoveItem-Methode lässt sich ein vorhandenes Objekt schnell und einfach löschen - entweder durch Angabe des Key-Wertes oder durch Angabe der Position innerhalb der Collection. Um auf ein Objekt zuzugreifen verwendet man die Items-Auflistung, die gleichzeitig die Standard-Eigenschaft des Collection-Objekts darstellt. Der Items-Auflistung muss man hierbei entweder den Key-Wert des Objekts übergeben oder die Position des Objekts innerhalb der Collection.
In unserem Fall verwenden wir das Collection-Objekt als Sammelbehälter für unsere einzelnen Browser-Fenster (MDIChild-Formen).
Das Collection-Objekt selbst wird hierbei in der Hauptanwendung, unserer MDIForm, verwaltet. Fügen Sie daherfolgende Deklaration in den Allgemein-Teil der MDIForm ein:
Option Explicit ' "Sammelbehälter" für unsere Child-Formen Dim oChilds As New Collection
Speichern einer neuen Child-Form im Collection-Objekt
Wird eine neue Child-Form erstellt, speichern wir eine Referenz auf die Form in unserem Collection-Objekt. Als Key-Wert (Schlüsselwert) verwenden wir hierbei eine fortlaufendeNummerierung, die wir gleichzeitig auch in der Tag-Eigenschaft der Child-Form speichern. Somit können wir dann später anhand der ID (laufendeNummerierung) bequem auf die Child-Form zugreifen.
Public Sub ChildAdd(oForm As Form) ' Child-Window der Collection hinzufügen ' als Key-Wert verwenden wir eine fortlaufende ' Nummerierung, die wir zusätzlich noch in der ' Tag-Eigenschaft der Child-Form speichern Static nID As Long ' fortlaufende Nummer erzeugen nID = nID + 1 ' Child-Form der Collection hinzufügen oChilds.Add oForm, "k" & CStr(nID) ' ID-Wert zusätzlich in der Tag-Eigenschaft ' der Child-Form speichern oForm.Tag = nID ' Form-Caption in die ComboBox eintragen With Combo1 .AddItem oForm.Caption .ItemData(.NewIndex) = nID .Enabled = True End With End Sub
Hinweis:
Der Key-Wert eines Collection-Eintrags muss immer alphanumerisch beginnen. Aus diesem Grund setzen wir einfach den Buchstaben k vor unsere laufendeNummerierung.
Frage: Wann muss die ChildAdd-Prozedur aufgerufen werden?
Genau! Immer dann, wenn eine neue Child-Form erstellt wurde. Fügen wir also folgenden Aufruf in das Form_Load-Ereignis der Child-Form (frmBrowser) ein:
Private Sub Form_Load() ' Hauptanwendung mitteilen, dass eine neue ' Child-Form geladen wurde Me.Caption = "Neues Browser-Fenster" MDIForm1.ChildAdd Me End Sub
Prüfen, ob eine Child-Form geladen ist
Wenn Sie innerhalb der Hauptanwendung (MDIForm) prüfen möchten, ob überhaupt ein Browser-Fenster geöffnet ist, geht das jetzt unter Einbeziehung des Collection-Objekts wesentlich einfacher und eleganter:
If oChild.Count = 0 Then ' kein Child-Form geladen Combo1.ListIndex = -1 Combo1.Enabled = False End Sub
Entfernen einer Child-Form aus dem Collection-Objekt
Wird eine Child-Form geschlossen, muss die jeweilige Form-Referenz aus dem Collection-Objekt entfernt werden. Hierfür benötigen wir die laufende Nummer der Child-Form, die wir ja glücklicherweise auch kennen (Tag-Eigenschaft der Form!).
Public Sub ChildRemove(oForm As Form) ' ChildForm aus Collection entfernen Dim nID As Long Dim i As Long ' laufende Nummer der Form nID = oForm.Tag ' Eintrag aus der ComboBox entfernen With Combo1 For i = 0 To .ListCount - 1 If .ItemData(i) = nID Then .RemoveItem i Exit For End If Next i End With ' Form-Referenz aus dem Collection-Objekt entfernen oChilds.Remove "k" & CStr(nID) ' ComboBox deaktivieren, falls keine Child-Form ' mehr geöffnet If oChilds.Count = 0 Then Combo1.Enabled = False End Sub
Frage: Wann muss die ChildRemove-Prozedur aufgerufen werden?
Genau! Immer dann, wenn eine Child-Form geschlossen wird. Fügen wir also folgenden Aufruf in das Form_Unload-Ereignis der Child-Form (frmBrowser) ein:
Private Sub Form_Unload(Cancel As Integer) ' Hauptanwendung mitteilen, dass eine ' Child-Form geschlossen wurde MDIForm1.ChildRemove Me End Sub
Aktivieren einer bestimmten Child-Form
Nachdem nun alle geöffneten Child-Formen in der ComboBox angezeigt werden, soll natürlich immer die aktive Child-Form in der ComboBox selektiert sein. Ebenso soll bei Auswahl einer anderen Child-Form diese in den Vordergrund gebracht werden und den Fokus erhalten.
Erweitern wir also den Code unserer MDIForm um folgende neue Prozedur:
Public Sub ChildActivate(ByVal nID As Long) ' bestimmte ChildForm aktivieren und in ' den Vordergrund bringen oChilds("k" & CStr(nID)).SetFocus ' aktive Form in der ComboBox anzeigen Dim i As Long With Combo1 For i = 0 To .ListCount - 1 If .ItemData(i) = nID Then If .ListIndex <> i Then .ListIndex = i Exit For End If Next i End With End Sub
Frage: Wann muss die ChildActivate-Prozedur aufgerufen werden?
Genau! Immer dann, wenn eine Child-Form durch den Anwender aktiviert wurde und natürlich auch dann, wenn der Anwender eine bestimmte Child-Form aus der ComboBox-Liste ausgewählt hat. Fügen wir also folgenden Code in das Form_Activate-Ereignis der Child-Form (frmBrowser) ein:
Private Sub Form_Activate() ' Hauptanwendung mitteilen, welche Child-Form ' aktiviert wurde MDIForm1.ChildActivate Me.Tag End Sub
Und jetzt noch nachfolgenden Code in den Codeteil der MDIForm einfügen:
Private Sub Combo1_Click() ' ChildForm aktivieren und in den Vordergrund bringen ChildActivate Combo1.ItemData(Combo1.ListIndex) End Sub
Neues Browser-Fenster öffnen
Beim Klick auf den CommandButton (cmdNew) soll ein neues Browser-Fenster geöffnet und angezeigt werden. Das neue Fenster soll natürlich auch in der ComboBox erscheinen. Alles, was wir hierfür brauchen, ist folgender 2-Zeiler:
Private Sub cmdNew_Click() ' Neues Browser-Fenster öffnen Dim oForm As New frmBrowser oForm.Show End Sub
Nicht mehr und nicht weniger. Das neue Child-Window wird autom. dem Collection-Objekt hinzugefügt, da im Form_Load-Ereignis die Prozedur ChildAdd autom. ausgeführt wird.
Ändern der Titelzeile einer Child-Form
Beim Laden einer Webseite sollte in der Titelzeile der Form nicht "frmBrowser" angezeigt werden, sondern vielmehr der Titel der Webseite. Die Titelzeile der Webseite lässt sich wie folgt ermitteln:
Private Sub WebBrowser1_TitleChange(ByVal Text As String) ' Titel der Webseite in der Titelzeile der Form anzeigen Me.Caption = Text MDIForm1.ChildRename Me.Tag End Sub
Natürlich muss jetzt auch der entsprechende Eintrag in der ComboBox der MDIForm geändert werden. Dies erledigt die Prozedur ChildRename innerhalb der MDIForm:
Public Sub ChildRename(ByVal nID As Long) ' Titelzeile der ChildForm auslesen und ' den dazugehörigen ComboBox-Eintrag ändern Dim i As Long With Combo1 ' alle Einträge durchlaufen... For i = 0 To .ListCount - 1 ' ... und mit "nID" vergleichen If .ItemData(i) = nID Then ' Titelzeile der Form aus der Collection auslesen .List(i) = oChilds("k" & CStr(nID)).Caption End If Next i End With End Sub
Schließen aller Child-Formen
Um alle geöffneten Child-Formen zu schließen, müssen wir lediglich das Collection-Objekt "durchlaufen" und die einzelnen enthaltenen Objekte (Childs) entladen:
Public Sub ChildCloseAll() ' Alle ChildForms schließen Do While oChilds.Count > 0 Unload oChilds(oChilds.Count) Loop End Sub
Fazit:
Wie aus dem Workshop ersichtlich, lassen sich die Child-Formen einer MDI-Anwendung mit Hilfe eines Collection-Objekts sehr einfach verwalten. Die Möglichkeit jedem einzelnen Element des Collection-Objekts einen eindeutigen Key-Wert zuordnen zu können, mit dessen Hilfe man auf ein Objekt zugreifen kann, macht das Collection-Objekt nicht nur in Zusammenhang mit dem Speichern von Form-Objekt-Referenzen zu einer äußerst interessanten Angelegenheit.
In diesem Sinne: Let's go collecting Childforms