|
Sind Sie auch schon an dieser Fehlermeldung verzweifelt, wenn Sie versucht haben, eine Referenz auf das UserControl-Objekt als Parameter einer Methode (etwa einer Klasse) oder Funktion (in einem Standard-Modul) zu übergeben? Wenn dieser Versuch nicht vergebens wäre, könnten Sie vom anvisierten Ziel aus über die Eigenschaften, Methoden und Ereignisse des UserControl-Objekts verfügen - gerade so, wie Sie etwa über ein Form verfügen können, dessen Referenz Sie weitergereicht haben. Damit wir uns nicht missverstehen: Gemeint ist der Zugriff auf das UserControl-Objekt selbst (zum Beispiel der Zugriff auf UserControl.BackColor usw.), und nicht auf das Control, das Sie damit gerade programmieren wollen.
Nach ein paar Experimenten werden auch Sie vielleicht darauf gekommen sein, dass die Übergabe funktioniert, wenn der Parameter als Variant deklariert und die Referenz als Wert (ByVal) übergeben wird:
Public Sub TuWas(ByVal UserControlObject As Variant)
Nun können Sie auf die Methoden und Eigenschaften des UserControl-Objekts zugreifen:
UserControlObject.BackColor = vbRed
End Sub
Sogar das Ablegen der so übergebenen Referenz auf das UserControl-Objekt zur weiteren Verwendung in einer lokalen Objekt-Variablen des Datentyps Object funktioniert:
Private mUserControl As Object
Public Sub StoreUserControl(ByVal UserControlObject As Variant)
Set mUserControl = UserControlObject
End Sub
Public Sub TuWas()
mUserControl.BackColor = vbCyan
End Sub
Immerhin können wir so mit der Referenz einiges anfangen, auch wenn wir sie so leider nur in so genannter "später" Bindung verwenden können.
Damit bleibt nun noch ein Punkt zu knacken: die Deklaration der Objekt-Variablen "As UserControl". Damit würden wir zum einen die so genannte "frühe" (und wesentlich schnellere) Bindung erreichen.
Hoppla, Sie wissen nicht, was es mit dieser frühen oder späten "Pünktlichkeit" der Bindung anscheinend auf sich hat? Okay, entweder gedulden Sie sich, bis ich einmal die Muße finde, darüber einen Artikel zu schreiben. Oder Sie lesen baldmöglichst ein sehr empfehlenswertes Buch und lernen damit auch noch das gesamte Fundament der COM-/ActiveX-Programmierung mit Visual Basic kennen: "com/activex-komponenten...".
Nun, weiter im Kontext: Zum anderen könnten wir aus der lokalen Objekt-Variablen auch einen Ereignisempfänger machen. Wie ich im Artikel Lausch-Eingriffe gezeigt habe, können Sie ohne weiteres über eine als Ereignisempfänger per WithEvents ... As Form deklarierte Objekt-Variable die Ereignisse eines Forms außerhalb desselben abfangen. Doch der analoge Versuch, das gleiche mit einem UserControl-Objekt zu veranstalten (WithEvents ... As UserControl), geht schief.
Das heißt, nicht ganz - bei der Deklaration der Objekt-Variablen macht uns Visual Basic nämlich noch keinen Strich durch die Rechnung. Aber beim Versuch der Zuweisung der doch so elegant übergebenen Referenz bekommen wir wieder eine frustrierende Fehlermeldung vor die Nase gesetzt: "Laufzeitfehler 13 - Typen unverträglich". Ich habe wohl so ziemlich alle Möglichkeiten durchgespielt - Deklarationen variiert, mit Konvertierfunktionen und deren Parametern herumgespielt, mit ByVal und ByRef... - eine letztlich so simple Möglichkeit wie das obenstehende "ByVal As Variant" scheint es hierfür nicht zu geben - oder kennen Sie zufällig eine? Dann nichts wie her damit...!
Schließlich habe ich aber doch noch eine Lösung gefunden. Sie beruht darauf, die übergebene Objekt-Referenz per Zeiger-Kopie einer als UserControl deklarierten Objekt-Variablen unterzujubeln und damit zugleich von allem Ballast, der zuvor bei unseren Versuchen die VB-Alarmglocke schrillen ließ, zu befreien. Dazu verwenden wir die API-Funktion RtlMoveMemory (die üblicherweise wie auch sinnfälligerweise per Alias-Deklaration in CopyMemory umgetauft wird) und die undokumentierte VB-Funktion ObjPtr, die den Referenz-Zeiger auf eine Objekt-Variable liefert. Dieses Kopier-Verfahren wird in der COM-Programmierung des öfteren genutzt, wenn mit nicht gerade VB-freundlichen bzw. nicht VB-konformen COM-Schnittstellen und Typbibliotheken hantiert wird. Erwarten Sie aber bitte nicht, dass ich hier weiter darauf eingehe - das würde an dieser Stelle zu weit führen.
Jedoch habe ich, wie immer, für Sie die notwendigen Code-Zeilen in eine Hilfsfunktion gepackt. Damit wir unser Gedächtnis nicht überstrapazieren, wenn wir die Funktion benötigen, habe ich sie CUcl genannt - ganz auf der Linie von CInt, CVar, CDate und dergleichen.
Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" _
(VarDest As Any, VarSource As Any, ByVal BytesToCopy As Long)
Public Function CUcl(ByVal iUCL As Variant) As UserControl
Dim nUCL As UserControl
CopyMemory nUCL, ObjPtr(iUCL), 4
Set CUcl = nUCL
CopyMemory nUCL, 0&, 4
End Function
Das Belauschen der inneren Ereignisse eines UserControls in einer Klasse oder einem anderen UserControl geht nun wie gewohnt und fast wie von selbst vonstatten. In einer lauschenden Klasse (etwa namens cHookUserControl) sähe der Code beispielsweise so aus:
Private WithEvents eUserControl As UserControl
Public Sub Init(UserControlObject As UserControl)
Set eUserControl = UserControlObject
End Sub
Private eUserControl_Click()
MsgBox "Extern abgefangener Click auf ein UserControl"
End Sub
Und im UserControl konvertieren Sie das UserControl-Objekt mit CUcl, bevor Sie die Referenz an eine Klasse nach dem obenstehenden Muster übergeben:
Private pHookUCL As cHookUserControl
Private Sub UserControl_ReadProperties(PropBag As PropertyBag)
Set pHookUCL = New cHookUserControl
pHookUCL.Init CUcl(UserControl)
End Sub
Das gleiche Verfahren können Sie übrigens auch für das UserDocument-Objekt verwenden.
|