vb@rchiv
VB Classic
VB.NET
ADO.NET
VBA
C#

https://www.vbarchiv.net
Rubrik: Forms/Controls   |   VB-Versionen: VB5, VB601.03.05
Dynamisches Hinzufügen von Controls mit Klasse

Mit diesem Workshop wollen wir Ihnen zeigen, wie Sie Controls zu Ihrer Anwendung dynamisch hinzufügen und gleichzeitig auch auf die Control-Ereignisse reagieren können, ohne hierfür ein Steuerelementfeld erstellen zu müssen.

Autor:  Wolfgang ChristBewertung:  Views:  34.265 

Mit diesem Workshop wollen wir Ihnen zeigen, wie Sie Controls zu Ihrer Anwendung dynamisch hinzufügen und gleichzeitig auch auf die Control-Ereignisse reagieren können, ohne hierfür ein Steuerelementfeld erstellen zu müssen.

Viele von Ihnen werden nun sagen, dass dies ein alter Hut ist und doch sehr einfach funktioniert. Wir möchten Ihnen aber einen Weg zeigen, wie Sie dies vollkommen dynamisch realisieren und auf alle Events reagieren können, wenn Sie wollen.

Voraussetzung hierfür wäre, dass Sie den Workshop  Polymorphismus (Schnittstellenimplementierung) bereits durchgearbeitet haben.

  1. Das Prinzip
  2. Wir definieren uns unsere Schnittstelle
  3. Implements IDynamicControl
  4. Eine Collection
  5. clsDynamicControl
  6. Abfangen und Feuern
  7. Reagieren
  8. Ein kleines Beispiel

1. Das Prinzip

Das Prinzip zu dieser Methode ist im Grunde genommen relativ einfach. Wir definieren über eine Schnittstelle dem Container der Controls (der Form) weitere Ereignisse bzw. Methoden hinzu, die wir innerhalb der Form implementieren müssen. Diese Ereignisse repräsentieren alle Events der hinzugefügten Controls, auf die wir während der Laufzeit reagieren wollen. Nun fügen wir alle gewünschten neuen Controls während der Laufzeit über eine Klasse hinzu, in welche das Control seine Events feuert. Diese fangen wir ab und "feuern" in die Form.

Die hier vorgestellte Methode zur Realisierung kann um beliebig viele Controls erweitert werden. Um den Rahmen dieses Workshops nicht zu sprengen, werden wir uns auf zwei Control-Typen beschränken (Button und Textbox).

2. Wir definieren uns unsere Schnittstelle

An dieser Stelle müssen wir definieren, auf welche Events der Controls wir reagieren wollen. Wir definieren dies als Methoden innerhalb unserer Schnittstelle, und übergeben unsere oben genannte Klasse. Auf welche Ereignisse wollen wir reagieren ?

Nun für den Button fällt mir eigentlich nur eines ein, das Click-Ereignis.
Für die Textbox fallen mir adhoc das Click-, Lost/GotFocus- und KeyPress-Ereignis ein.

Wir starten ein neues Projekt und fügen ein neues Klassenmodul ein. Dies nennen wir IDynamicControl. Dort fügen wir einfach nur folgenden Code ein:

Option Explicit
 
Public Sub KeyPress(ByVal oEventSource As clsDynamicControl, ByRef KeyAscii As Integer)
  ' 
End Sub
 
Public Sub Click(ByVal oEventSource As clsDynamicControl)
  ' 
End Sub
 
Public Sub GotFocus(ByVal oEventSource As clsDynamicControl)
  ' 
End Sub
 
Public Sub LostFocus(ByVal oEventSource As clsDynamicControl)
  ' 
End Sub

Wie bereits gesagt, dies ist nur ein kleines Beispiel zum Erläutern und Verdeutlichen des Prinzips, das sich beliebig erweitern lässt.

3. Implements IDynamicControl

In unserer Form, auf der wir Controls dynamisch hinzufügen wollen, fügen wir im Allgemeinteil folgenden Code hinzu:

Private oDynControls As clsDynamicControl ' mehr dazu später
Implements IDynamicControls

Und im Form_Load-Event fügen wir folgende Zeilen ein:

Private Sub Form_Load()
  Set oDynControls = New clsDynamicControl
  oDynControls.Form = Me
End Sub

Was bedeutet dies nun?

Nun mittels Implements erklären wir VB, dass innerhalb unserer Form alle Methoden der Schnittstelle als Events der Schnittstelle hinzugefügt werden müssen. Im Code Editor ist unter Objekten ein neues vorhanden, IDynamicControl. Nun müssen alle Funktionen mit Code belegt werden, und wenn es nur ein Kommentar ist. Mehr dazu im bereits bezeichneten Workshop  Polymorphismus (Schnittstellenimplementierung).

4. Eine Collection

Wie bereits in der Einleitung geschrieben, wollen wir eine vollkommen dynamische Form. Aus diesem Grunde wäre es schön, wenn wir auch nur ein zusätzliches Objekt deklarieren müssen, in welchem alle dynamisch hinzugefügten Controls "behandelt" werden. Da wir gleichzeitig alles kapseln möchten, können wir hier nur eine Klasse verwenden, in der eine Collection alle einzelnen Objekte aufnimmt.

Wir fügen also eine weitere Klasse hinzu, diese nennen wir clsDynamicControls.

Innerhalb dieser Klasse benötigen wir Eigenschaften zur Übergabe der gewünschten Form, des implementierten Interfaces und zum Zugriff auf die hinzugefügten Objekte, ebenso wie Funktionen zum Hinzufügen und Entfernen von Controls. Fügen wir in diese Klasse einfach folgenden Code ein, die jeweiligen Kommentare sollten alles Nötige zum Verstehen aussagen.

Option Explicit
 
Private m_oControls As Collection
Private m_oForm As Form
Private m_oInterface As IDynamicControl
Private m_lID As Long
Private Sub Class_Initialize()
  Set m_oControls = New Collection
End Sub
 
Private Sub Class_Terminate()
  Set m_oControls = Nothing
End Sub
' Eigenschaften ' Stuff
Public Property Get Interface() As IDynamicControl
  Set Interface = m_oInterface
End Property
 
Public Property Set Interface(Value As IDynamicControl)
  Set m_oInterface = Value
End Property
 
Public Property Get Form() As Form
  Set Form = m_oForm
End Property
 
Public Property Set Form(Value As Form)
  Set m_oForm = Value
End Property
' Eigenschaften für Objekte
Public Property Get GetControl(ByVal sName As String) As clsDynamicControl
  ' Objekt über Name aus Collection ermitteln und zurückgeben
  Set GetControl = m_oControls.Item(sName)   
End Property
' Nicht öffentliche Methoden
Private Sub Inc(ByRef lID As Long)
  lID = lID + 1
End Sub
 
Private Function GetID() As Long
  Inc m_lID
  GetID = m_lID
End Function
' Öffentliche Methoden
Public Function AddControl(ByVal ControlType As eTypeOfControl, _
  ByVal sControlName As String) As clsDynamicControl
 
  Dim oControl As Control
  Dim oDynControl As clsDynamicControl  
 
  ' evtl. Leerzeichen abschneiden
  sControlName = Trim(sControlName)
 
  If Len(sControlName) = 0 Then
    sControlName = "oControl" & GetID
  End If
 
  ' Objekt vom Typ clsDynamicControl erstellen und einrichten
  Set oDynControl = New clsDynamicControl
  With oDynControl
    .ControlType = ControlType
    Set .Form = m_oForm
    Set .Interface = m_oInterface
    .Name = sControlName
    Set oControl = .CreateControl
  End With
 
  ' Objekt der Collection hinzufügen
  m_oControls.Add oDynControl, sControlName
 
  Set AddControl = oDynControl
 
  Set oControl = Nothing
  Set oDynControl = Nothing  
End Function
 
Public Function DeleteControl(ByVal sControlName As String)
  ' entfernt ein bereits bestehendes Control
  ' sofern vorhanden ...
  m_oControls.Remove sControlName  
End Function

5. clsDynamicControl

Diese Klasse wird unsere dynamischen Controls beinhalten sowie alle Events der Controls abfangen und weiterreichen. Wollen wir weitere Control-Typen hinzufügen müssen wir nur diese Klasse anpassen.

Wir fügen in unser Projekt ein neues Klassenmodul ein und nennen es clsDynamicControl.

Als Erstes definieren wir, welche Controls wir überhaupt dynamisch hinzufügen wollen; in unserem Beispiel Button und Textboxen.

Option Explicit
 
Private WithEvents m_oTextBox As TextBox
Private WithEvents m_oButton As CommandButton
 
Public Enum eTypeOfControl
  eTextBox
  eButton
End Enum

Des Weiteren noch ein paar private Variablen der Klasse:

' Welches Control wurde mit dieser Klasse hinzugefügt
Private pControlType As eTypeOfControl 
' Wurde das Control bereits erzeugt
Private bCreated As Boolean
' Auf welcher Form?
Private m_oForm As Form
' Welches Interface
Private m_oInterface As IDynamicControl
' Welchen Namen soll das Control erhalten
Private m_sName As String
' Eigenschaften
Public Property Get Name() As String
  Name = m_sName
End Property
 
Public Property Let Name(Value As String)
  m_sName = Value
End Property
 
Public Property Get Interface() As IDynamicControl
  Set Interface = m_oInterface
End Property
 
Public Property Set Interface(Value As IDynamicControl)
  Set m_oInterface = Value
End Property
 
Public Property Get Form() As Form
  Set Form = m_oForm
End Property
 
Public Property Set Form(Value As Form)
  Set m_oForm = Value
End Property
 
Public Property Let ControlType(ByVal vData As eTypeOfControl)
  pControlType = vData
End Property
 
Public Property Get ControlType() As eTypeOfControl
  ControlType = pControlType
End Property
' Für den direkten Zugriff auf das erzeugte Control
Public Property Get ControlObject() As Control
  Select Case pControlType
    Case eTypeOfControl.eTextBox
      Set ControlObject = m_oTextBox
    Case eTypeOfControl.eButton
      Set ControlObject = m_oButton
  End Select
End Property

Nun fehlen noch die benötigten privaten Funktionen der Klasse:

' Wurde alles Benötigte übergeben?
Private Function AllOK() As Boolean
  AllOK = False
  If m_oForm Is Nothing Then Exit Function
  If m_oInterface Is Nothing Then Exit Function
  If Len(Trim(m_sName)) < 1 Then Exit Function
 
  Select Case pControlType
    Case eTypeOfControl.eButton
    Case eTypeOfControl.eTextBox
    Case Else
      Exit Function
  End Select
  AllOK = True
End Function
' Welchen Klassennamen hat das gewünschte Control?
Private Function GetControlClassName()
  Select Case pControlType
    Case eTypeOfControl.eTextBox
      GetControlClassName = "VB.TextBox"
    Case eTypeOfControl.eButton
      GetControlClassName = "VB.CommandButton"
  End Select
End Function
' Und natürlich die obligatorischen Funktionen
Private Sub Class_Initialize()
  bCreated = False
End Sub
 
Private Sub Class_Terminate()
  DestroyControl
End Sub

Nun fehlen natürlich noch die Funktionen zum Anlegen und Zerstören der Controls:

' Control erzeugen
Public Function CreateControl() As Control
 
  ' Wenn nicht alles korrekt ist, darf das Control nicht erzeugt werden
  If Not AllOK Then
    Err.Raise 1001, "CreateControl", "Control konnte nicht erstellt werden"
    Exit Function
  End If
 
  ' nur ein Control pro Instanz der Klasse
  If bCreated Then
    ' Control wurde bereits erstellt, bereits erstelltes 
    ' Control zurückgeben
    Set CreateControl = Me.ControlObject
    Exit Function
  End If
 
  bCreated = True
  Select Case pControlType
    Case eTypeOfControl.eTextBox
      Set m_oTextBox = m_oForm.Controls.Add(GetControlClassName, m_sName)
      Set CreateControl = m_oTextBox
 
    Case eTypeOfControl.eButton
      Set m_oButton = m_oForm.Controls.Add(GetControlClassName, m_sName)
      Set CreateControl = m_oButton
 
  End Select
End Function
' Control wieder zerstören, aber nur wenn es auch angelegt wurde
Public Sub DestroyControl()
  If Not bCreated Then
    m_oForm.Controls.Remove m_sName
    Set m_oTextBox = Nothing
    Set m_oButton = Nothing
    bCreated = False
  End If
End Sub

6. Abfangen und feuern

Nun haben wir alles was wir benötigen, um beliebig viele Controls während der Laufzeit anzulegen und wieder zu entfernen. Aber da dies uns nicht reicht und wir möchten, dass wir auf die Events dieser Controls reagieren können, wenn wir wollen, bedienen wir uns eines kleinen Tricks. Wir haben die Controls mittels des Schlüsselwortes WithEvents deklariert. Dies bedeutet, dass wir alle Events dieses Controls innerhalb der Klasse empfangen können. Um diese weiterreichen zu können, haben wir mittels Implements unserer Form weitere Funktionen im Sinne von haben müssen hinzudefiniert. Da wir diese Funktionen nun in der Form haben definieren müssen, können wir diese jetzt immer weiterreichen und die Klasse selber mit übergeben.

Aus diesem Grunde haben wir unser Interface immer mit übergeben; dies ist nun in m_oInterface verfügbar.

' TextBox Events
Private Sub m_oTextBox_Click()
  Call m_oInterface.Click(Me)
End Sub
 
Private Sub m_oTextBox_GotFocus()
  Call m_oInterface.GotFocus(Me)
End Sub
 
Private Sub m_oTextBox_KeyPress(KeyAscii As Integer)
  Call m_oInterface.KeyPress(Me, KeyAscii)
End Sub
 
Private Sub m_oTextBox_LostFocus()
  Call m_oInterface.LostFocus(Me)
End Sub
' Button Events
Private Sub m_oButton_Click()
  Call m_oInterface.Click(Me)
End Sub
 
Private Sub m_oButton_GotFocus()
  Call m_oInterface.GotFocus(Me)
End Sub
 
Private Sub m_oButton_KeyPress(KeyAscii As Integer)
  Call m_oInterface.KeyPress(Me, KeyAscii)
End Sub
 
Private Sub m_oButton_LostFocus()
  Call m_oInterface.LostFocus(Me)
End Sub

7. Regaieren

Unsere Form haben wir, wie im 3. Abschnitt beschrieben, mittels Implements um weitere Funktionen erweitert, in diesen können wir nun die Ereignisse empfangen. Das Control selber wird in der übergebenen Klasse "mitgeliefert".

8. Ein kleines Beispiel

Erstellen Sie ein neues Projekt und fügen Sie die drei Klassen wie beschrieben ein. Danach fügen Sie einfach folgenden Code in den Code-Teil von Form1 ein und starten die Anwendung. Die Funktionsweise selber wird im Quellcode erklärt.

Option Explicit
 
' Interface
Implements IDynamicControl
 
' Dynamische Controls
Private oDynamicControls As clsDynamicControls
Private Sub Form_Load()
  ' Aufbauen der Oberfläche
  Set oDynamicControls = New clsDynamicControls
  Dim oDynControl As clsDynamicControl
 
  ' Form und Interface übergeben
  With oDynamicControls
    Set .Form = Me
    Set .Interface = Me
  End With
 
  ' Eine Textbox anlegen
  Set oDynControl = oDynamicControls.AddControl(eTextBox, "DynamischeTextBox1")
  With oDynControl
    ' Verschieben 
    .ControlObject.Move 240, 240, 2535, 375
    ' Sichtbar machen
    .ControlObject.Visible = True
    ' Eigenschaft setzen
    .ControlObject.Text = .Name
  End With
 
  ' Einen Button anlegen
  Set oDynControl = oDynamicControls.AddControl(eButton, "DynamischerButton1")
  With oDynControl
    ' Verschieben, Sichtbar und eine Eigenschaft
    .ControlObject.Move 240, 840, 2535, 375
    .ControlObject.Visible = True
    .ControlObject.Caption = "Dynamischer Button"
  End With
End Sub
' Ereignisse
Private Sub IDynamicControl_KeyPress(ByVal oEventSource As clsDynamicControl, _
  KeyAscii As Integer)
 
  ' nur zur Demonstration gedacht, es können auch Parameter 
  ' der Ereignisse übergeben werden
  Debug.Print oEventSource.Name & "_" & "KeyPress(" & CStr(KeyAscii) & ")"
End Sub
 
Private Sub IDynamicControl_LostFocus(ByVal oEventSource As clsDynamicControl)
  Debug.Print oEventSource.Name & "_LostFocus"
 
  If oEventSource.ControlType = eTextBox Then
    oEventSource.ControlObject.BackColor = vbWhite
  End If
End Sub
 
Private Sub IDynamicControl_Click(ByVal oEventSource As clsDynamicControl)
  Debug.Print oEventSource.Name & "_Click"
 
  If oEventSource.ControlType = eButton Then
    ' Die Textbox disablen
    With oDynamicControls.GetControl("DynamischeTextBox1").ControlObject
      .Enabled = Not .Enabled
    End With
  End If
End Sub
 
Private Sub IDynamicControl_GotFocus(ByVal oEventSource As clsDynamicControl)
  Debug.Print oEventSource.Name & "_GotFocus"
 
  If oEventSource.ControlType = eTextBox Then
    oEventSource.ControlObject.BackColor = vbRed
  End If
End Sub

Nun wünschen wir Ihnen viel Spaß mit diesen Möglichkeiten.
 



Anzeige

Kauftipp Unser Dauerbrenner!Diesen und auch alle anderen Workshops finden Sie auch auf unserer aktuellen vb@rchiv  Vol.6
(einschl. Beispielprojekt!)

Ein absolutes Muss - Geballtes Wissen aus mehr als 8 Jahren vb@rchiv!
- nahezu alle Tipps & Tricks und Workshops mit Beispielprojekten
- Symbol-Galerie mit mehr als 3.200 Icons im modernen Look
Weitere Infos - 4 Entwickler-Vollversionen (u.a. sevFTP für .NET), Online-Update-Funktion u.v.m.
 
 
Copyright ©2000-2024 vb@rchiv Dieter OtterAlle Rechte vorbehalten.


Microsoft, Windows und Visual Basic sind entweder eingetragene Marken oder Marken der Microsoft Corporation in den USA und/oder anderen Ländern. Weitere auf dieser Homepage aufgeführten Produkt- und Firmennamen können geschützte Marken ihrer jeweiligen Inhaber sein.