|
Mittels der API-Funktion SetParent können Sie ein Windows-Fenster (etwa ein Form oder ein Steuerelement) in ein anderes Fenster verschieben. In der Regel brauchen Sie es nicht wieder in das ursprüngliche Elternfenster zurück schieben, wenn Sie die Anwendung beenden und die betroffenen Forms dabei entladen werden. Manchmal jedoch reagiert Visual Basic etwas allergisch auf eine solche Unterlassung. Genauer gesagt, es gibt Steuerelemente, die allergisch reagieren, wenn sie verschoben und nicht wieder zurück geschoben werden bzw. wenn ihnen ein anderes Fenster untergeschoben wird, ohne dass es wieder entfernt wird. Und allergische Reaktionen bedeuten meistens eine Schutzverletzung und damit einen ungewollten, vorzeitigen Absturz der Anwendung. Auch wenn diese sowieso beendet werden sollte, ist ein solches Ende sicher nicht die feine Art. Erstens nervt die Fehlermeldung und sieht mit Sicherheit wenig professionell aus. Und zweitens kann nach einem Absturz wie immer die Stabilität des Gesamtsystems beeinträchtigt sein.
Um nun ein Fenster wieder in sein ursprüngliches Elternfenster zurück schieben zu können, benötigen Sie dessen Handle. Meistens wissen Sie sowieso, welches Form oder Steuerelement das ursprüngliche Elternfenster war und können das Handle aus dessen hWnd-Eigenschaft auslesen. Doch falls Sie es nicht wissen sollten, weil sich das vielleicht erst während des Programmablaufs ergeben sollte, müssen Sie sich das Handle entweder vor dem Verschieben merken können - etwa in einer Variable im entsprechenden Gültigkeitsbereich. Wenn Sie viele verschiedene Fenster verschieben wollen, kann das Management der Merkvariablen kompliziert werden. Einfacher wäre es, wenn sich jedes Fenster selbst merken könnte, wohin es ursprünglich gehört hatte.
Sie können das Handle des ursprünglichen Elternfensters beispielsweise in der Tag-Eigenschaft eines Steuerelements ablegen. Sie können aber auch den kleinen Extra-Datenbereich verwenden, über den jedes Windows-Fenster verfügt, falls dieser nicht schon anderweitig belegt sein sollte. Weitere Einzelheiten finden Sie dazu in "Extra-Tag per API".
Praktisch wäre es jetzt noch, wenn Sie diesen Merkvorgang automatisieren könnten. Die Funktion SetParent kommt Ihnen dazu bereits ein Stückchen entgegen, indem sie Ihnen als Rückgabewert das Handle des vorhergehenden Elternfensters zurückgibt. Sie brauchen in einer Hilfsfunktion nun nur noch zu prüfen, ob der besagte Extra-Datenbereich bereits verwendet wird. Wenn nicht, können Sie den Rückgabewert von SetParent dorthin schreiben. Falls aber doch, erhalten Sie von der Hilfsfunktion das Handle des vorhergehenden Elternfensters als Rückgabewert und können es so immerhin noch selbst verwalten.
Private Declare Function GetWindowLong Lib "user32" _
Alias "GetWindowLongA" (ByVal hWnd As Long, _
ByVal nIndex As Long) As Long
Private Declare Function SetParent Lib "user32" _
(ByVal hWndChild As Long, ByVal hWndNewParent As Long) As Long
Private Declare Function SetWindowLong Lib "user32" _
Alias "SetWindowLongA" (ByVal hWnd As Long, _
ByVal nIndex As Long, ByVal dwNewLong As Any) As Long
Private Const GWL_USERDATA = (-21)
Public Function SetParentEx(ByVal ChildWnd As Long, _
ByVal NewParentWnd As Long) As Long
Dim nOldParentWnd As Long
Dim nUserData As Long
nOldParentWnd = SetParent(ChildWnd, NewParentWnd)
nUserData = GetWindowLong(ChildWnd, GWL_USERDATA)
If nUserData = 0 Then
SetWindowLong ChildWnd, GWL_USERDATA, nOldParentWnd
Else
SetParentEx = nOldParentWnd
End If
End Function
Für den Rückweg steht die Funktion ResetParent zur Verfügung. Übergeben Sie im optionalen Parameter OldParentWnd ein Handle, wird diese Vorgabe für den Aufruf von SetParent verwendet. Lassen sie ihn weg, versucht ResetParent den Datenbereich auszulesen und den vorgefundenen Wert als Handle des neuen "alten" Elternfensters zu verwenden. Wurde kein Wert vorgefunden, gibt ResetParent um dieses zu signalisieren den Wert True zurück.
Public Function ResetParent(ByVal ChildWnd As Long, _
Optional ByVal OldParentWnd As Long) As Boolean
Dim nOldParentWnd As Long
If OldParentWnd > 0 Then
nOldParentWnd = OldParentWnd
Else
nOldParentWnd = GetWindowLong(ChildWnd, GWL_USERDATA)
SetWindowLong ChildWnd, GWL_USERDATA, 0&
End If
If nOldParentWnd Then
SetParent ChildWnd, nOldParentWnd
Else
ResetParent = True
End If
End Function
Wenn Sie das in dem Extra-Datenbereich abgelegte Handle des Elternfensters zwischendurch einfach mal so in Erfahrung bringen möchten, verwenden Sie die folgende Funktion GetOldParentWnd.
Public Function GetOldParentWnd(ByVal ChildWnd As Long) As Long
GetOldParentWnd = GetWindowLong(ChildWnd, GWL_USERDATA)
End Function
|