ABOUT Visual Basic Programmieren Programmierung Download Downloads Tips & Tricks Tipps & Tricks Know-How Praxis VB VBA Visual Basic for Applications VBS VBScript Scripting Windows ActiveX COM OLE API ComputerPC Microsoft Office Microsoft Office 97 Office 2000 Access Word Winword Excel Outlook Addins ASP Active Server Pages COMAddIns ActiveX-Controls OCX UserControl UserDocument Komponenten DLL EXE
Diese Seite wurde zuletzt aktualisiert am 04.05.2000

Diese Seite wurde zuletzt aktualisiert am 04.05.2000
Aktuell im ABOUT Visual Basic-MagazinGrundlagenwissen und TechnologienKnow How, Tipps und Tricks rund um Visual BasicActiveX-Komponenten, Controls, Klassen und mehr...AddIns für die Visual Basic-IDE und die VBA-IDEVBA-Programmierung in MS-Office und anderen AnwendungenScripting-Praxis für den Windows Scripting Host und das Scripting-ControlTools, Komponenten und Dienstleistungen des MarktesRessourcen für Programmierer (Bücher, Job-Börse)Dies&Das...

Themen und Stichwörter im ABOUT Visual Basic-Magazin
Code, Beispiele, Komponenten, Tools im Überblick, Shareware, Freeware
Ihre Service-Seite, Termine, Job-Börse
Melden Sie sich an, um in den vollen Genuss des ABOUT Visual Basic-Magazins zu kommen!
Informationen zum ABOUT Visual Basic-Magazin, Kontakt und Impressum

Zurück...

Dynamische Container

Zurück...


Anzeige

Von Martin Szugat mailto:ms_dyncontainer@aboutvb.de

Steuerelement-Arrays sind einerseits ein Segen, erlauben sie doch in Kombination mit der Load-Anweisung die Programmierung von dynamischen Formularen und UserControls. Andererseits entpuppen sich Steuerelement-Arrays nur zu oft fast als Fluch, denn bei der Anwendung der Anweisungen Load und Unload warten zahlreiche Fallen auf Sie.

So gibt es zum Beispiel Probleme, wenn Sie dynamisch in ein Steuerelement-Array geladene Steuerelemente in ein weiteres, ebenfalls dynamisch in ein Steuerelement-Array geladenes Container-Steuerelement verschieben - sie diesem also unterzuordnen. Bei dem Versuch, dieses Container-Steuerelement mittels der Unload-Anweisung wieder zu entladen, werden Sie mit der Fehlermeldung "Entladen in diesem Kontext nicht möglich" konfrontiert. Ein Blick in die Dokumentation zu dem Fehler mit der Nummer 365 fördert zwar eine ausführlichere Beschreibung der Fehlermeldung zu Tage, jedoch stimmen die dort aufgeführten, möglichen Ursachen für die Fehlermeldung nicht mit der eben beschriebenen Situation überein. Die tatsächliche Ursache für den Laufzeitfehler sind in diesem Fall die untergeordneten Steuerelemente. Erst wenn diese untergeordneten Steuerelemente aus dem betroffenen Steuerelement entfernt werden, indem sie entweder entladen werden oder in ein anderes Containersteuerelement beziehungsweise direkt in das allen übergeordnete Parent-Objekt (meist wohl ein Form) als Container verschoben werden, lässt sich das betroffene Steuerelement per Unload-Anweisung aus dem Speicher und dem Parent-Container hinaus befördern.

Ein Parent-Container, der dynamisch geladene und teils verschachtelte Steuerelemente beheimatet, müsste sich folglich "merken", wer wem untergeordnet oder übergeordnet ist. Der Verwaltungsaufwand hierfür ist enorm - eine universelle Lösung, bei der die konkrete Verschachtelung der Steuerelemente nicht fest codiert zu werden braucht, wäre weitaus eleganter und praktikabler. Ein erster Lösungsansatz könnte daher etwa so aussehen:

Public Sub UnloadCtl(ByVal Object As Object)
  Dim Control As Control
  For Each Control In Object.Parent.Controls
    If Control.Container Is Object Then
      UnloadCtl Control
    End If
  Next Control
  Unload Object
End Sub

Diese Prozedur UnloadCtl wird nun anstelle der Unload-Anweisung aus Visual Basic verwendet, um Steuerelemente aus Steuerelement-Arrays zu entfernen. Sie durchläuft sämtliche Steuerelemente des Parent-Containers und prüft bei jedem, ob vielleicht sein spezifischer Container das zu entladende Steuerelements ist. Falls der Container übereinstimmt, wird das aktuelle Steuerelement über einen rekursiven Aufruf der UnloadCtl-Prozedur entladen. Dabei werden natürlich auch wiederum dessen untergeordnete Steuerelemente entladen sowie die Steuerelemente unterhalb der untergeordneten Steuerelemente und so weiter und so fort. Das eigentlich zu entladende Steuerelement wird schließlich ganz zum Schluss über einen Aufruf der Unload-Anweisung entladen.

Diese vorläufige Implementierung der UnloadCtl-Prozedur hat allerdings eine offensichtliche Schwäche und einen groben Fehler. Die Schwäche offenbart sich, wenn sich auf in dem Parent-Container, der das zu entladende Steuerelement enthält, eine größere Anzahl an weiteren Steuerelementen befindet. Je mehr Steuerelemente das Formular beheimatet, um so schlechter wird das Laufzeitverhalten der Prozedur. Aufgrund der Rekursion steigt die Anzahl der notwendigen Schleifendurchgänge mit jedem zusätzlichen Steuerelement nicht linear, sondern sogar expotenzial an.

Jener erwähnte Fehler tritt hingegen nur unter bestimmten Bedingungen auf. Wenn ein Steuerelement mittels der Unload-Anweisung entladen wird, verschwindet es auch aus der Controls-Auflistung des Formulars. Allerdings erzeugt eine For-Each-Schleife eine nur für die Dauer der For-Each-Schleife gültige Auflistung, die von der Laufvariablen durchlaufen wird und die von der Unload-Anweisung nicht berührt und nicht verändert wird. Infolgedessen liefert die temporäre Auflistung unter Umständen eine Referenz auf ein nicht mehr gültiges Steuerelement. Den Versuch über die Laufvariable auf das ungültige Steuerelement zu zugreifen, quittiert Visual Basic mit dem Laufzeitfehler 340.

Es gibt jedoch eine andere, wenngleich weitaus komplizierte Lösung. Die zweite Fassung der UnloadCtl-Prozedur gleicht der ersten Fassung zumindest oberflächlich. Unter der Haube leistet diese zweite Variante der Prozedur wesentlich mehr als der erste Versuch einer Implementierung.

So wurde das Laufzeitverhalten dahingehend verbessert, dass eine steigende Anzahl an Steuerelementen nur mehr einen linearen Anstieg der Schleifendurchgänge zur Folge hat. Des weiteren wurde der eben beschriebene Fehler dadurch gelöst, dass zuerst sämtliche zu entladenden Steuerelemente aufgespürt und vermerkt werden und diese dann erst zum Schluss in einem Durchgang entladen werden. Dabei kommen diejenigen Steuerelemente, die sich in der Hierarchie weiter unten befinden, zuerst zum Zuge und erst dann folgen jeweils deren Containersteuerelemente.

Public Sub UnloadCtl(ByVal Object As Object, _
  Optional ByVal UnloadSubControls As Boolean = False)

  Dim nSubControls() As Collection
  Dim nControl As Control
  Dim nParent As Object
  Dim nContainer As Object
  Dim i As Long
  Dim z As Long
  
  If UnloadSubControls Then
    ReDim nSubControls(1 To 5) As Collection
    Set nParent = Object.Parent
    For Each nControl In nParent.Controls
      Set nContainer = nControl
      z = 0
      Do
        Set nContainer = nContainer.Container
        z = z + 1
      Loop Until nContainer Is nParent Or nContainer Is Object
      If nContainer Is Object Then
        If z > UBound(nSubControls) Then
          ReDim Preserve nSubControls(1 To z) As Collection
        End If
        If nSubControls(z) Is Nothing Then
          Set nSubControls(z) = New Collection
        End If
        nSubControls(z).Add nControl
      End If
    Next 'nControl
    For i = UBound(nSubControls) To 1 Step -1
      If Not (nSubControls(i) Is Nothing) Then
        For Each nControl In nSubControls(i)
          Unload nControl
        Next 'nControl
      End If
    Next i
  End If
  Unload Object
End Sub

Für die Aufnahme der Referenzen auf die zu entladenden Steuerelemente deklariert die Unload-Prozedur ein dynamisches Array. Das Array enthält jedoch selbst nicht die Referenzen. Statt dessen wird das Array mit Collection-Objekten gefüllt, die wiederum die Referenzen auf die zu entladenden Steuerelementen aufnehmen. Dabei werden in der ersten Collection diejenigen Steuerelemente abgelegt, die sich direkt unter dem eigentlich zu entladenden Steuerelement befinden. In der zweiten Collection werden hingegen diejenigen Steuerelemente abgelegt, die sich unterhalb derjenigen Steuerelemente befinden, die in der ersten Collection abgelegt wurden, und so fort. Demnach entspricht der Index der Collections im Array der Tiefe der Steuerelemente auf dem Formular relativ zum obersten zu entladenden Steuerelement. Unter der Annahme, dass die meisten Anwendungen mit fünf Ebenen auskommen, wird das Array dementsprechend vordimensioniert - die Größe des Arrays wird aber bei Bedarf automatisch erhöht. Um Speicher und Zeit zu sparen, wird die Instanzbildung der jeweils benötigten Collections auf einen späteren Zeitpunkt verschoben.

Wie schon bei der ersten Lösung werden sämtliche Steuerelemente auf dem Formular in einer For-Each-Schleife durchlaufen. In einer zweiten Schleife erfolgt ausgehend vom aktuellen Steuerelement eine Bewegung in der Hierarchie der Steuerelemente aufwärts, in dem die Container-Eigenschaft des aktuellen Containersteuerelements abfragt werden, bis entweder das Parent-Objekt oder das zu entladende Steuerelement erreicht wird. Bei jedem Schleifendurchgang wird die Zählervariable z inkrementiert, so dass am Ende die Tiefe des aktuellen Steuerelements in der Hierarchie bekannt ist. Diese Information ist notwendig, um das aktuelle Steuerelement in der richtigen Collection abzulegen, falls das Steuerelement sich unterhalb des zu entladenden Steuerelements befindet. Doch bevor das Steuerelement der jeweiligen Collection hinzugefügt werden kann, ist noch zu prüfen, ob das Array mit den Collections der Tiefe des Steuerelements entsprechend groß dimensioniert wurde bzw. ob das Array für diese Stufe bereits eine Collection bereithält. Bei Bedarf wird also das Array vergrößert und mit einem zusätzlichem Collection-Objekt belegt. Anschließend kann das Steuerelement der entsprechenden Collection des Arrays hinzugefügt werden.

Nachdem sämtliche Steuerelemente auf dem Formular durchlaufen wurden, wird das Array nach Collection-Objekten durchsucht. Dabei wird es von hinten nach vorne durchlaufen, so dass die Steuerelemente in tieferliegenden Schichten zuerst entladen werden. Das Entladen der Steuerelemente erfolgt wie gewohnt mittels der Unload-Anweisung in einer weiteren For-Each-Schleife, die auf die aktuelle Collection angewandt wird. Zu guter Letzt wird noch das eigentlich zu entladende Steuerelement per Unload eliminiert.


Beispiel-Projekt und Modul modDynContainer (dyncontainer.zip - ca. 3 KB)


Artikel
Zum Download-Bereich dieses Artikel
Mail an den Autor dieses Artikels

KnowHow
Zur KnowHow-Übersicht

KnowHow-Themen
Themen - Allgemeines
Themen - Entwicklungsumgebung (VB-IDE)
Themen - Forms
Themen - Steuerelemente (Controls)
Themen - Grafik
Themen - Dateien
Themen - UserControls
Themen - Einsteiger-Tipps
Themen - Wussten Sie...?

Übersicht nach Titeln in alphabetischer Reihenfolge
Übersicht nach Erscheinungsdatum

Schnellsuche



Zum Seitenanfang

Copyright © 1999 - 2017 Harald M. Genauck, ip-pro gmbh  /  Impressum

Zum Seitenanfang

Zurück...

Zurück...

Download Internet Explorer