Anzeige
Anzeige
HERBERS
Excel-Forum (Archiv)
20+ Jahre Excel-Kompetenz: Von Anwendern, für Anwender
Anzeige
Inhaltsverzeichnis

Controls mit Events in Collections verwalten

Forumthread: Controls mit Events in Collections verwalten

Controls mit Events in Collections verwalten
08.05.2026 14:15:37
Sumbu
Ich hatte das vor zig Jahren schonmal gemacht, habe aber vergessen wie das funktioniert: Ich möchte ein zweidimensionales Array aus Textboxen erzeugen, und es in zwei ineinander verschachtelten Collections verwalten, bzw. auch erzeugen. Ich weiß noch, dass ich die Textboxen in der .Add-Methode einer der Klassen erzeugt habe, und dabei den unten stehenden, selbstgebastelten Code für die Erzeugung in einem Standardmodul verwendet habe.

Public Function newControl(ByRef pvobjParent As Object, ByVal pvstrNamen As String, ByVal pvstrType As String, _

Optional ByVal pvsngLeft, Optional ByVal pvsngTop, Optional ByVal pvsngHeight, Optional ByVal pvsngWidth, _
Optional ByVal pvintSFX, Optional ByVal pvintBorderStyle, Optional ByVal pvlngHGFarbe, Optional ByVal pvlngVGFarbe, Optional ByVal pvstrCaption) As Control

Set newControl = pvobjParent.Controls.Add("Forms." & pvstrType & ".1", pvstrNamen, True)
With newControl
If Not IsMissing(pvsngLeft) Then .Left = pvsngLeft
If Not IsMissing(pvsngTop) Then .Top = pvsngTop
If Not IsMissing(pvsngHeight) Then .Height = pvsngHeight
If Not IsMissing(pvsngWidth) Then .Width = pvsngWidth
If Not IsMissing(pvintSFX) Then .SpecialEffect = pvintSFX
If Not IsMissing(pvintBorderStyle) Then .BorderStyle = pvintBorderStyle
If Not IsMissing(pvlngHGFarbe) Then .BackColor = pvlngHGFarbe
If Not IsMissing(pvlngVGFarbe) Then .ForeColor = pvlngVGFarbe
If Not IsMissing(pvstrCaption) Then
On Error Resume Next
.Caption = pvstrCaption
.Text = pvstrCaption
On Error GoTo 0
End If
End With
End Function


Was ich nicht mehr weiß, ist wie ich die Textboxen in die Collections einbinde, und wie ich die Events verwalte. Da brauche ich vermutlich noch eine Watcher-Klasse, aber wie binde ich die dann ein. Mein VBA ist leider ein bissel eingerostet^^

Wenn mir jemand helfen kann: Vielen Dank schon im Voraus!
Anzeige

14
Beiträge zum Forumthread
Beiträge zu diesem Forumthread

Betreff
Datum
Anwender
Anzeige
AW: Controls mit Events in Collections verwalten
08.05.2026 22:55:58
Alwin Weisangler
Hallo,

die gezeigte Funktion erzeugt lediglich die gewünschten Controls, entsprechend den mitgegebenen Parametern. Das ist soweit klar.
Was für einem Außenstehenden unklar ist, was du mit den Werten der Controls machen willst.

Hier mal ein Beispiel von mir wie in 2 Klassen die Inhalte von Textboxen addiert werden, und per Button in die passende Zelle die Summe übergeben wird.
Das Beispiel mag so ziemlich sinnfrei sein, da man dies natürlich viel einfacher in den Zellen rechnen lassen kann, zeigt aber wie man mit Klassen arbeiten kann.

Vielleicht hilft dir das ja schon etwas weiter.
Ansonsten Datei hochladen, und beschreiben, was erreicht werden soll. Momentan habe ich leider wenig Zeit mich um solche Sachen zu kümmern.
https://www.herber.de/bbs/user/180685.xlsm

Gruß Uwe


Anzeige
AW: Controls mit Events in Collections verwalten
09.05.2026 03:58:45
Sumbu
Hallo Uwe,

richtig, die gezeigte Funktion erzeugt nur die gewünschten Controls. Aber mit dieser Funktion ist das Erzeugen ziemlich praktisch, weil man damit in einer verschachtelten Schleife sehr einfach ein Grid erzeugen kann, wo man genau die Größe und Position der Controls (In diesem Fall, geht es mir um Textboxen) festlegen kann.

Bei den Werten geht es nur darum, sie in einer Minidatenbank zu speichern, und beim Start wieder einzulesen. Der Code dazu:

in einem Standardmodul (basClasses):
Option Explicit

Option Private Module

Public NoEvent As Boolean

Public ProductType As clsProductType
Public SingleProduct As clsSingleProduct
Public gGridForm As frmGridEditor
'Public gCtlWatchers As Collection ' hält Instanzen von clsControlEvents / clsWatchEvents
Public idCount As Long
Private mCols As Long, mRows As Long

Private Type ProdType
prodTypeID As String * 20
SingProdID As String * 20
ProdTypeName As String * 25
SingProdName As String * 25
SingProdBackCol As String * 8
SingProdForeCol As String * 8
End Type

Public Sub ShowProductGrid()
' ProductType muss gefüllt sein (z. B. durch testProductsClasses)
If Not classIsInitialized(ProductType) Then Set ProductType = New clsProductType

'mRows = IIf(ProductType Is Nothing, 0, ProductType.Count)
'mCols = singleProductTotalCount()

Set gGridForm = New frmGridEditor
Set gCtlWatchers = New Collection

gGridForm.Show vbModeless

'BuildGrid mRows, mCols


End Sub
Public Sub testProductClasses()
'Mit diesem Sub lassen sich die sich die Klassen zu Testzwecken mit Beispieldaten füllen
Dim rows As Long, cols As Long, X As Long, Y As Long, aCounter As Long, bCounter As Long

rows = 5
cols = 4

'bCounter = 1

Set ProductType = New clsProductType

For X = 1 To rows

Call ProductType.Add("X" & Left("00000" & CStr(X), 6), "Name" & CStr(X))

Set SingleProduct = New clsSingleProduct

For Y = 1 To cols

Call ProductType(X).SingleProduct.Add(createID, "Produkt" & CStr(Y), 65535, 0)

Next Y

Next X

End Sub



Public Function createID() As String
Dim Y As Long
'If idCount = 10 Then Stop
idCount = idCount + 1
createID = "X" & Right("00000" & CStr(idCount), 6)
End Function

Public Sub writeProducts() 'Speichert die Klasse ProductType.SingleProduct in einer Minidatenbank
Dim X As Long, Y As Long
Dim Intfile As Integer
Dim TypeConvert As ProdType
Dim myPath As String

Call testProductClasses

Const myFilename As String = "CartData.mealcards"

myPath = MainPath & "DATA\" & myFilename

Dim pvstrFiller As String

For X = 1 To 25
pvstrFiller = pvstrFiller & Chr(12)
Next X

If IsFileOpen(myPath) = 0 Then Kill myPath
Intfile = FreeFile

Open myPath For Random As #Intfile Len = Len(TypeConvert)

For X = 1 To ProductType.Count
For Y = 1 To ProductType(X).SingleProduct.Count
With TypeConvert
.prodTypeID = Right(pvstrFiller & CStr(ProductType(X).ID), 20)
.ProdTypeName = Right(pvstrFiller & CStr(ProductType(X).Name), 25)
.SingProdID = Right(pvstrFiller & CStr(ProductType(X).SingleProduct(Y).ID), 20)
.SingProdName = Right(pvstrFiller & CStr(ProductType(X).SingleProduct(Y).Name), 25)
.SingProdBackCol = Right(pvstrFiller & CStr(ProductType(X).SingleProduct(Y).BackColor), 8)
.SingProdForeCol = Right(pvstrFiller & CStr(ProductType(X).SingleProduct(Y).ForeColor), 8)
End With
Put #Intfile, , TypeConvert
Next Y
Next X

Close #Intfile

End Sub



Public Sub readProductsALT()
Dim X As Long, Intfile As Integer, myPath As String, TypeConvert As ProdType
Const myFilename As String = "CartData.mealcards"

On Error Resume Next

myPath = MainPath & "DATA\" & myFilename

If Not classIsInitialized(ProductType) Then
Set ProductType = New clsProductType
Else
Set ProductType = Nothing
Set ProductType = New clsProductType
End If

Intfile = FreeFile
If Dir(myPath) = "" Then Exit Sub

Open myPath For Random As #Intfile Len = Len(TypeConvert)

Do While Not EOF(Intfile)
X = X + 1
Get #Intfile, , TypeConvert
With TypeConvert
Call ProductType.Add(removeChr12(.prodTypeID), removeChr12(.ProdTypeName))
Do




Loop
End With
Loop
End Sub


Public Sub readProducts()
Dim TypeConvert As ProdType
Dim Intfile As Integer
Dim myPath As String
Const myFilename As String = "CartData.mealcards"
Dim existing As clsProductType

myPath = MainPath & "DATA\" & myFilename

If Not classIsInitialized(ProductType) Then
Set ProductType = New clsProductType
Else
Set ProductType = Nothing
Set ProductType = New clsProductType
End If

Intfile = FreeFile
If Dir(myPath) = "" Then Exit Sub

Open myPath For Random As #Intfile Len = Len(TypeConvert)

On Error GoTo Cleanup

Do While Not EOF(Intfile)
Get #Intfile, , TypeConvert

' ProdTypeID bereinigen (falls Chr(12) am Anfang vorhanden)
Dim prodTypeID As String
prodTypeID = CStr(removeChr12(TypeConvert.prodTypeID))

' Prüfen, ob ProdType bereits existiert
On Error Resume Next
Set existing = ProductType.Item(prodTypeID)
If Err.Number > 0 Then
Err.Clear
'Neu anlegen (Name bereinigen)
Call ProductType.Add(prodTypeID, CStr(removeChr12(TypeConvert.ProdTypeName)))
Set existing = ProductType.Item(removeChr12(prodTypeID))
Else
'ggf. Name ergänzen, falls leer
If existing.Name = "" Then existing.Name = CStr(removeChr12(TypeConvert.ProdTypeName))
End If
On Error GoTo Cleanup

'SingleProduct zur gefundenen/erstellten ProductType-Instanz hinzufügen
With TypeConvert
existing.SingleProduct.Add _
CStr(removeChr12(.SingProdID)), _
CStr(removeChr12(.SingProdName)), _
CLng(removeChr12(.SingProdBackCol)), _
CLng(removeChr12(.SingProdForeCol))
End With

Set existing = Nothing
Loop

Cleanup:
If Err.Number > 0 Then
'Fehlerbehandlung: optional Debug-Ausgabe
Debug.Print "readProducts Fehler: " & Err.Number & " - " & Err.Description
Err.Clear
End If

If Intfile > 0 Then Close #Intfile
End Sub
Public Function removeChr12(pvString As String) As String
If Len(pvString) = 0 Then
removeChr12 = pvString
Exit Function
End If

Do While Len(pvString) > 0 And Left$(pvString, 1) = Chr$(12)
pvString = Mid$(pvString, 2)
Loop

removeChr12 = pvString
End Function

Public Function IsFileOpen(filename As String) As Integer
Dim filenum As Integer, errnum As Integer
On Error Resume Next
filenum = FreeFile()
Open filename For Input Lock Read As #filenum
Close filenum
errnum = Err
On Error GoTo 0
IsFileOpen = errnum
End Function

Public Function singleProductTotalCount() As Long
Dim X As Long, Y As Long

singleProductTotalCount = 0

For X = 1 To ProductType.Count
If singleProductTotalCount ProductType(X).SingleProduct.Count Then
singleProductTotalCount = ProductType(X).SingleProduct.Count
End If
Next X

singleProductTotalCount = singleProductTotalCount + 1
End Function



Public Function classIsInitialized(pvobjClass As Object) As Boolean
Dim ID_Check As String
On Error GoTo errorhandler

ID_Check = ProductType(1).ID

classIsInitialized = True

Exit Function

errorhandler:
classIsInitialized = False

End Function


In einem Klassenmodul (clsProductType):
Option Explicit

Option Compare Text

'Modulname: clsProductType

Private mstrID As String
Private mstrName As String
Private mobjSingleProduct As clsSingleProduct
Private mobjProductType As Collection

Private Sub Class_Initialize()
Set ProductType = New Collection
Set SingleProduct = New clsSingleProduct
End Sub

Private Sub Class_Terminate()
Set ProductType = Nothing
Set SingleProduct = Nothing
End Sub

Friend Property Get ID() As String
ID = mstrID
End Property
Friend Property Let ID(ByVal pvstrID As String)
mstrID = pvstrID
End Property

Friend Property Get Name() As String
Name = mstrName
End Property
Friend Property Let Name(ByVal pvstrName As String)
mstrName = pvstrName
End Property

Friend Property Get ProductType() As Collection
Set ProductType = mobjProductType
End Property
Friend Property Set ProductType(ByRef pvobjProductType As Collection)
Set mobjProductType = pvobjProductType
End Property

Friend Property Get SingleProduct() As clsSingleProduct
Set SingleProduct = mobjSingleProduct
End Property
Friend Property Set SingleProduct(ByRef pvobjSingleProduct As clsSingleProduct)
Set mobjSingleProduct = pvobjSingleProduct
End Property

Friend Sub Add( _
ByVal pvstrID As String, ByVal pvstrName As String)

Dim objProductTypeClass As clsProductType

Set objProductTypeClass = New clsProductType

With objProductTypeClass
.ID = pvstrID
.Name = pvstrName
Set .SingleProduct = New clsSingleProduct
End With

Call ProductType.Add(Item:=objProductTypeClass, key:=pvstrID)

Set objProductTypeClass = Nothing

End Sub

Friend Sub Move( _
ByVal pvvntIndex As Variant, _
Optional ByVal opvvntBefore As Variant = Empty, _
Optional ByVal opvvntAfter As Variant = Empty)
Dim objProductType As clsProductType
If Not IsEmpty(opvvntBefore) Then
With Me
Set objProductType = .Item(pvvntIndex)
Call .Delete(pvvntIndex)
Call ProductType.Add(Item:=objProductType, key:=pvvntIndex, Before:=opvvntBefore)
End With
Set objProductType = Nothing
ElseIf Not IsEmpty(opvvntAfter) Then
With Me
Set objProductType = .Item(pvvntIndex)
Call .Delete(pvvntIndex)
Call ProductType.Add(Item:=objProductType, key:=pvvntIndex, Before:=opvvntAfter)
End With
Set objProductType = Nothing
Else
Call MsgBox("Fehlender Parameter in der Move-Methode der " & _
"ProductType-Klasse.", vbCritical, "Methode fehlgeschlagen")
End If
End Sub

Private Function SortString(ByVal pvlngIndex As Long) As String
SortString = Item(pvlngIndex).Name
End Function

Friend Sub Sort( _
Optional ByVal opvenmSortOrder As XlSortOrder = xlAscending, _
Optional ByVal opvvntLBound As Variant, _
Optional ByVal opvvntUBound As Variant)
Dim lngIndex1 As Long, lngIndex2 As Long
Dim vntBuffer As Variant
If IsMissing(opvvntLBound) Then opvvntLBound = 1
If IsMissing(opvvntUBound) Then opvvntUBound = Count
lngIndex1 = opvvntLBound
lngIndex2 = opvvntUBound
vntBuffer = SortString((lngIndex1 + lngIndex2) \ 2)
Do
If opvenmSortOrder = xlAscending Then
Do While SortString(lngIndex1) vntBuffer
lngIndex1 = lngIndex1 + 1
Loop
Do While vntBuffer SortString(lngIndex2)
lngIndex2 = lngIndex2 - 1
Loop
Else
Do While SortString(lngIndex1) > vntBuffer
lngIndex1 = lngIndex1 + 1
Loop
Do While vntBuffer > SortString(lngIndex2)
lngIndex2 = lngIndex2 - 1
Loop
End If
If lngIndex1 lngIndex2 Then
Call Move(pvvntIndex:=Item(lngIndex1).ID, _
opvvntBefore:=Item(lngIndex2).ID)
Call Move(pvvntIndex:=Item(lngIndex2).ID, _
opvvntBefore:=Item(lngIndex1).ID)
lngIndex1 = lngIndex1 + 1
lngIndex2 = lngIndex2 - 1
ElseIf lngIndex1 = lngIndex2 Then
lngIndex1 = lngIndex1 + 1
lngIndex2 = lngIndex2 - 1
End If
Loop Until lngIndex1 > lngIndex2
If opvvntLBound lngIndex2 Then Call Sort( _
opvenmSortOrder, opvvntLBound, lngIndex2)
If lngIndex1 opvvntUBound Then Call Sort( _
opvenmSortOrder, lngIndex1, opvvntUBound)
End Sub

Friend Function Count() As Long
Count = ProductType.Count
End Function

Friend Sub Delete(ByVal pvvntIndex As Variant)
Call ProductType.Remove(pvvntIndex)
End Sub

Public Function Item(ByVal pvvntIndex As Variant) As clsProductType
Set Item = ProductType.Item(pvvntIndex)
End Function

Public Function NewEnum() As IUnknown
Set NewEnum = ProductType.[_NewEnum]
End Function


Und in einem weitereren Klassenmodul die untergeordnete Klasse (clsSingleProduct):
Option Explicit

Option Compare Text

'Modulname: clsSingleProduct

Private mstrID As String
Private mstrName As String
Private mlngBackColor As Long
Private mlngForeColor As Long
Private mobjSingleProduct As Collection

Private Sub Class_Initialize()
Set SingleProduct = New Collection
End Sub

Private Sub Class_Terminate()
Set SingleProduct = Nothing
End Sub

Friend Property Get ID() As String
ID = mstrID
End Property
Friend Property Let ID(ByVal pvstrID As String)
mstrID = pvstrID
End Property

Friend Property Get Name() As String
Name = mstrName
End Property
Friend Property Let Name(ByVal pvstrName As String)
mstrName = pvstrName
End Property

Friend Property Get BackColor() As Long
BackColor = mlngBackColor
End Property
Friend Property Let BackColor(ByVal pvlngBackColor As Long)
mlngBackColor = pvlngBackColor
End Property

Friend Property Get ForeColor() As Long
ForeColor = mlngForeColor
End Property
Friend Property Let ForeColor(ByVal pvlngForeColor As Long)
mlngForeColor = pvlngForeColor
End Property

Friend Property Get SingleProduct() As Collection
Set SingleProduct = mobjSingleProduct
End Property
Friend Property Set SingleProduct(ByRef pvobjSingleProduct As Collection)
Set mobjSingleProduct = pvobjSingleProduct
End Property

Friend Sub Add( _
ByVal pvstrID As String, ByVal pvstrName As String, ByVal pvlngBackColor As Long, ByVal pvlngForeColor As Long)

Dim objSingleProductClass As clsSingleProduct

Set objSingleProductClass = New clsSingleProduct

With objSingleProductClass
.ID = pvstrID
.Name = pvstrName
.BackColor = pvlngBackColor
.ForeColor = pvlngForeColor
End With

Call SingleProduct.Add(Item:=objSingleProductClass, key:=pvstrID)

Set objSingleProductClass = Nothing

End Sub

Friend Sub Move( _
ByVal pvvntIndex As Variant, _
Optional ByVal opvvntBefore As Variant = Empty, _
Optional ByVal opvvntAfter As Variant = Empty)
Dim objSingleProduct As clsSingleProduct
If Not IsEmpty(opvvntBefore) Then
With Me
Set objSingleProduct = .Item(pvvntIndex)
Call .Delete(pvvntIndex)
Call SingleProduct.Add(Item:=objSingleProduct, key:=pvvntIndex, Before:=opvvntBefore)
End With
Set objSingleProduct = Nothing
ElseIf Not IsEmpty(opvvntAfter) Then
With Me
Set objSingleProduct = .Item(pvvntIndex)
Call .Delete(pvvntIndex)
Call SingleProduct.Add(Item:=objSingleProduct, key:=pvvntIndex, Before:=opvvntAfter)
End With
Set objSingleProduct = Nothing
Else
Call MsgBox("Fehlender Parameter in der Move-Methode der " & _
"SingleProduct-Klasse.", vbCritical, "Methode fehlgeschlagen")
End If
End Sub

Private Function SortString(ByVal pvlngIndex As Long) As String
SortString = Item(pvlngIndex).Name
End Function

Friend Sub Sort( _
Optional ByVal opvenmSortOrder As XlSortOrder = xlAscending, _
Optional ByVal opvvntLBound As Variant, _
Optional ByVal opvvntUBound As Variant)
Dim lngIndex1 As Long, lngIndex2 As Long
Dim vntBuffer As Variant
If IsMissing(opvvntLBound) Then opvvntLBound = 1
If IsMissing(opvvntUBound) Then opvvntUBound = Count
lngIndex1 = opvvntLBound
lngIndex2 = opvvntUBound
vntBuffer = SortString((lngIndex1 + lngIndex2) \ 2)
Do
If opvenmSortOrder = xlAscending Then
Do While SortString(lngIndex1) vntBuffer
lngIndex1 = lngIndex1 + 1
Loop
Do While vntBuffer SortString(lngIndex2)
lngIndex2 = lngIndex2 - 1
Loop
Else
Do While SortString(lngIndex1) > vntBuffer
lngIndex1 = lngIndex1 + 1
Loop
Do While vntBuffer > SortString(lngIndex2)
lngIndex2 = lngIndex2 - 1
Loop
End If
If lngIndex1 lngIndex2 Then
Call Move(pvvntIndex:=Item(lngIndex1).ID, _
opvvntBefore:=Item(lngIndex2).ID)
Call Move(pvvntIndex:=Item(lngIndex2).ID, _
opvvntBefore:=Item(lngIndex1).ID)
lngIndex1 = lngIndex1 + 1
lngIndex2 = lngIndex2 - 1
ElseIf lngIndex1 = lngIndex2 Then
lngIndex1 = lngIndex1 + 1
lngIndex2 = lngIndex2 - 1
End If
Loop Until lngIndex1 > lngIndex2
If opvvntLBound lngIndex2 Then Call Sort( _
opvenmSortOrder, opvvntLBound, lngIndex2)
If lngIndex1 opvvntUBound Then Call Sort( _
opvenmSortOrder, lngIndex1, opvvntUBound)
End Sub

Friend Function Count() As Long
Count = SingleProduct.Count
End Function

Friend Sub Delete(ByVal pvvntIndex As Variant)
Call SingleProduct.Remove(pvvntIndex)
End Sub

Public Function Item(ByVal pvvntIndex As Variant) As clsSingleProduct
Set Item = SingleProduct.Item(pvvntIndex)
End Function

Public Function NewEnum() As IUnknown
Set NewEnum = SingleProduct.[_NewEnum]
End Function


Auf die Art verwalte ich die Werte des anvisierten Textbox-Arrays. Die Frage ist jetzt, wie ich mir die Textboxen in eigens dafür vorgesehenen Klassen erzeuge und verwalte. Also ich will sie nicht nur mit der newControl-Function erzeugen, sondern auch gleich mit = an eine Klasse übergeben.

Liebe Grüße Guido


Anzeige
AW: Controls mit Events in Collections verwalten
09.05.2026 13:49:06
Ulf
Hi,
muss das so kompliziert und I/O-abhängig? Den gleichen Effekt könntest du mit einem versteckten Tabellenblatt, dessen Zellen die Typdefinitionen übernehmen, haben.
Noch dazu kein Implementieren von Persistenz notwendig. Form-Aufruf: Zellenobjekte lesen...

hth
Ulf
AW: Controls mit Events in Collections verwalten
09.05.2026 14:29:23
Sumbu
Hallo Ulf.

danke erstmal für deine Antwort. Ja leider muss das so kompliziert sein. Ist ein größeres Projekt das ich nach 10 Jahren Pause umarbeiten möchte. Alles zusammengenommen komm ich auf über 10.000 Zeilen Code. Die Minidatenbanken brauche ich, um die Daten zentral auf dem Server für mehrere Clients verfügbar zu halten. Aber der I/O ist ja schon gelöst. Das funktioniert aktuell. Was ich grade nicht hinkriege ist dieses Textbox-Array In einer Userform. Habs auch schon mit Chat Gbt versucht, aber der Bot ufert leider total aus und macht alles noch viel komplizierter.

Das Schreiben zweier ineinander verschachtelter Collections ist nicht so das Problem, da hab ich mir ein kleines Tool gebastelt, wo man nur die Namen der Module angeben muss, und die Eigenschaften. Den Rest macht Excel automatisch.

Dass Textbox-Array kann man auch ganz einfach anlegen. Im Userform-Code:

Private Sub buildTBArray(myRows as long, myCols

Dim X as long, Y as long
For X = 1 to myRows
For y 1 = 1 to myCols
Me.Controls.Add(....

etc...
End Sub


Ich weiß nur nicht mehr, wie ich die Watcherklasse mit den Collections verbinde.

Ich danke dir für deine Bemühungen
Anzeige
AW: Controls mit Events in Collections verwalten
09.05.2026 18:33:51
snb
Ich würde vorschlagen das ganze mal erst abzuspecken....
z.B.

Sub M_snb()

M_ctrl UserForm1, Array("TextBox", "Beispiel", 12, 12, 18, 18, 1, 255, 255 ^ 2, "snb")
End Sub


Sub M_ctrl(it, sn)

With it.Controls.Add("Forms." & sn(0) & ".1", sn(1), True)
.Left = sn(2)
.Top = sn(3)
.Height = sn(4)
.Width = sn(5)
.BorderStyle = sn(6)
.BackColor = sn(7)
.ForeColor = sn(8)
If sn(0) = "Label" Then .Caption = sn(9)
End With
End Sub


Anzeige
AW: Controls mit Events in Collections verwalten
10.05.2026 00:21:15
Ulf
Hi,
Prinzip
Userform
Option Explicit


Public t As clsTextbox

Private Sub CommandButton1_Click()
Dim f As MSForms.TextBox
Set f = Me.Controls.Add("Forms.TextBox.1")
Set t = New clsTextbox
Set t.TxtBox = f
With t.TxtBox
.Left = 10
.Top = 10
.Name = "ID"
.Value = 10
End With
End Sub

Klasse
Option Explicit


Public WithEvents TxtBox As MSForms.TextBox


Private Sub TxtBox_KeyUp(ByVal KeyCode As MSForms.ReturnInteger, ByVal Shift As Integer)
MsgBox Me.TxtBox.Name
End Sub

hth
Ulf
Anzeige
AW: Controls mit Events in Collections verwalten
10.05.2026 09:14:23
Sumbu
Hallo snb, Hallo Ulf

snb, danke für die eine schön rote Textbox, aber was hat das mit der Überwachung der Events eines Textbox-Arrays zu tun?

Ulf, Du bist schon näher an meinem Problem dran, allerdings erzeugst du genau eine Textbox die du dann in der Watcher-Klasse überwachst. Ich brauche das allerdings für ein Array von Textboxen, damit ich die Werte an eine Datenklasse übergeben kann. Damit Du siehst was ich meine, hab ich mal einen kleinen Beispielcode geschrieben, in dem ich so ein Textbox-Array erszeuge.

Im Userform-Code:
Option Explicit


Private Sub UserForm_Initialize()

'Beispielwerte, ersetzen hier den jeweiligen .Count der Datenklassen:
Const mRows = 10
Const mCols = 5

With Me
.Height = mRows * 20
.Width = mCols * 60
.StartUpPosition = 0
.Caption = "GridEditor"
End With

Call fillTbGrid(mRows, mCols)

End Sub

Private Sub fillTbGrid(mRows As Long, mCols As Long)
Dim X As Long, Y As Long

For X = 1 To mRows
For Y = 1 To mCols
Call newControl(Me, "Tb" & CStr(X) & "-" & CStr(Y), _
"TextBox", (Y * 60 - 60), (X * 20 - 20), 23, 63, , 1)
Next Y
Next X

End Sub


In einem Standardmodul:
Option Explicit


Option Private Module

Public Function newControl(ByRef pvobjParent As Object, ByVal pvstrNamen As String, ByVal pvstrType As String, _
Optional ByVal pvsngLeft, Optional ByVal pvsngTop, Optional ByVal pvsngHeight, Optional ByVal pvsngWidth, _
Optional ByVal pvintSFX, Optional ByVal pvintBorderStyle, Optional ByVal pvlngHGFarbe, Optional ByVal pvlngVGFarbe, Optional ByVal pvstrCaption) As Control

Set newControl = pvobjParent.Controls.Add("Forms." & pvstrType & ".1", pvstrNamen, True)
With newControl
If Not IsMissing(pvsngLeft) Then .Left = pvsngLeft
If Not IsMissing(pvsngTop) Then .Top = pvsngTop
If Not IsMissing(pvsngHeight) Then .Height = pvsngHeight
If Not IsMissing(pvsngWidth) Then .Width = pvsngWidth
If Not IsMissing(pvintSFX) Then .SpecialEffect = pvintSFX
If Not IsMissing(pvintBorderStyle) Then .BorderStyle = pvintBorderStyle
If Not IsMissing(pvlngHGFarbe) Then .BackColor = pvlngHGFarbe
If Not IsMissing(pvlngVGFarbe) Then .ForeColor = pvlngVGFarbe
If Not IsMissing(pvstrCaption) Then
On Error Resume Next
.Caption = pvstrCaption
.Text = pvstrCaption
On Error GoTo 0
End If
End With
End Function


Vielen Dank schonmal und schönen Sonntag



Anzeige
AW: Controls mit Events in Collections verwalten
10.05.2026 15:15:58
Ulf
Hi,
mit Array ~
Modul:
Option Explicit


'Control-Variablen müssen Public sein, wenn sie für die Dauer der Ausführung gelten sollen
'Option Private Module

Public t() As clsTextbox
Public lngTnr As Long

Public Function newControl(ByRef pvobjParent As Object, ByVal pvstrNamen As String, ByVal pvstrType As String, _
Optional ByVal pvsngLeft, Optional ByVal pvsngTop, Optional ByVal pvsngHeight, Optional ByVal pvsngWidth, _
Optional ByVal pvintSFX, Optional ByVal pvintBorderStyle, Optional ByVal pvlngHGFarbe, Optional ByVal pvlngVGFarbe, Optional ByVal pvstrCaption) As Control

Set newControl = pvobjParent.Controls.Add("Forms." & pvstrType & ".1", pvstrNamen, True)
Set t(lngTnr) = New clsTextbox
With newControl
Set t(lngTnr).TxtBox = newControl
If Not IsMissing(pvsngLeft) Then .Left = pvsngLeft
If Not IsMissing(pvsngTop) Then .Top = pvsngTop
If Not IsMissing(pvsngHeight) Then .Height = pvsngHeight
If Not IsMissing(pvsngWidth) Then .Width = pvsngWidth
If Not IsMissing(pvintSFX) Then .SpecialEffect = pvintSFX
If Not IsMissing(pvintBorderStyle) Then .BorderStyle = pvintBorderStyle
If Not IsMissing(pvlngHGFarbe) Then .BackColor = pvlngHGFarbe
If Not IsMissing(pvlngVGFarbe) Then .ForeColor = pvlngVGFarbe
If Not IsMissing(pvstrCaption) Then
On Error Resume Next
.Caption = pvstrCaption
.Text = pvstrCaption
On Error GoTo 0
End If
End With
'Aktuelle Box hochzählen
lngTnr = lngTnr + 1
'
End Function

Userform:
Option Explicit


Private Sub UserForm_Initialize()

'Beispielwerte, ersetzen hier den jeweiligen .Count der Datenklassen:
Const mRows = 3
Const mCols = 3


With Me
.Height = mRows * 20 + 40
.Width = mCols * 60 + 20
.StartUpPosition = 0
.Caption = "GridEditor"
End With
'Aktuelle Box initialisieren
lngTnr = 0
'Anzahl Boxen
ReDim t((mRows * mCols) - 1)
Call fillTbGrid(mRows, mCols)

End Sub

Private Sub fillTbGrid(mRows As Long, mCols As Long)
Dim X As Long, Y As Long

For X = 1 To mRows
For Y = 1 To mCols
Call newControl(Me, "Tb" & CStr(X) & "-" & CStr(Y), _
"TextBox", (Y * 60 - 60), (X * 20 - 20), 23, 63, , 1)
Next Y
Next X

End Sub

Klasse:
Option Explicit


Public WithEvents TxtBox As MSForms.TextBox

Private Sub TxtBox_KeyUp(ByVal KeyCode As MSForms.ReturnInteger, ByVal Shift As Integer)
' Select Case TxtBox.Name
' Case "Tb1-1"
'
' Case "Tb3-1"
' '.....
' End Select
MsgBox Me.TxtBox.Name
End Sub

hth
Ulf
Anzeige
AW: Controls mit Events in Collections verwalten
10.05.2026 20:36:20
Sumbu
Hallo Ulf, hallo Case,

wir nähern uns der Lösung, aber so ganz klappt das noch nicht. Einfach für jede Textbox einen Public With Events Eintrag zu erstellen greift zu kurz, weil die Anzahl der Textboxen tatsächlich dynamisch ist. Dasselbe gilt auch für Select Case, aber Ulf, in deinem Sub finde ich den Ansatz gut nur ein Public With Events zu verwenden und im Sub zu bestimmen welche Textbox grade feuert. Das kann man über das Array lngTnr() bestimmen, oder über den Namen der Textbox, der im Sub fillTbGrid so erzeugt wird:

"Tb" & CStr(X) & "-" & CStr(Y)



Es gibt jedoch noch ein weiteres Problem: der Event springt nicht an, egal wie oft ich eine Taste drücke und wieder loslasse. Ich verstehe auch noch nicht ganz wieso der Event anspringen sollte. Kann man die Textboxen nicht irgendwie Bei Public With Events adden? Ich erinnere mich dunkel, dass es so eine Möglichkeit gab. Naja ist auch wirklich schon 10 Jahre her, dass ich mich mit der Problematik befasst habe.

Vielen Dank jedenfalls erstmal, vlt fällt euch ja noch etwas ein.

Grüße
Anzeige
AW: Controls mit Events in Collections verwalten
10.05.2026 23:37:59
Ulf
Hi,
Die Klasse muss auch clsTextBox heißen, i forgot. Die Konstanten mRows und mCols bestimmen die Anzahl der feuernden Events ((r*c)-1, da 0-basiert) und sind dynamisch.
Wenn du beim Erstellen den .Tag des Textfeldes um die Feldnamen zuzuweisen , kann man im Event mit .Tag=Feldname fallunterscheiden.
Genauso vorstellbar wäre die Ordinalzahl des Feldes zu nutzen usw
Die Zuweisungen
...
Set t(lngTnr) = New clsTextbox
With newControl
Set t(lngTnr).TxtBox = newControl
...
erzeugen eine leere Klasse und weisen die Textbox darin dem Control auf der Userform zu, dass muss so.
Innerhalb der Klasse kannst du alle Events einer Textbox verwenden.
Im Anhang der Anscheinsbeweis
https://www.herber.de/bbs/user/180699.xlsm
hth
Ulf
Anzeige
AW: Controls mit Events in Collections verwalten
11.05.2026 14:06:59
Sumbu
Problem gelöst und auch noch 2 sehr interessante Lösungen. Ulf, Deine Lösung ist glaub ich das, was ich den letzten 10 Jahren vergessen hatte. Case, Dein Ansatz alles nur Private zu deklarieren in der Klasse, Und die Control-Eigenschaften mit Property Let und Get zu verwenden ist auch sehr interessant. Ich muss jetzt ein bissel experimentieren.

Vielen Dank an alle die hier mitgeschrieben haben, und wie man mal wieder sieht: PEBKAC :D
Anzeige
In meinem Beispiel hat sich...
11.05.2026 15:04:38
Case
Moin, :-)

... eine "Ungereimtheit" eingeschlichen - merkt man nicht, ist aber nicht sauber. ;-)

Falls du mit Private/Friend/Let/Get experimentieren willst, dann merke dir: Außerhalb der Klasse werden die Properties genutzt - innerhalb der Klasse direkt die Member. ;-)
https://www.herber.de/bbs/user/180702.xlsb

Servus
Case


Anzeige
In meinem Beispiel...
11.05.2026 10:29:15
Case
Moin, :-)

... ist kein "Public". ;-)

Da ich - insbesondere in größeren Projekten - immer wieder Probleme mit den Klassen hatte, die auf "Public" basierten, nehme ich in meinen Projekten nur noch "Private" und "Friend". Das ist durch die Kapselung IMHO robuster. ;-)

Die Anzahl der TextBoxen kann ruhig dynamisch sein. ;-)

Servus
Case
Anzeige
Anzeige
Anzeige
Live-Forum - Die aktuellen Beiträge
Datum
Titel
14.05.2026 13:31:09
14.05.2026 09:50:42
13.05.2026 19:14:18