|
|
|
|
|
Sicher werden auch Sie sich schon gefragt haben, wie Sie das
eingebaute Kontext-Menü einer TextBox durch ein eigenes Popup-Menü
ersetzen können. Dazu werden unter anderem meistens zwei Lösungen
auf dem "Know-How-Markt gehandelt", die sie
vielleicht bereits kennen werden.
So wird verschiedentlich vorgeschlagen, im MouseUp-Ereignis der
TextBox diese vorübergehend zu sperren (Enabled = False),
das eigene Popup-Menü anzuzeigen, und die TextBox anschließend
wieder freizugeben (Enabled = True):
Private Sub TextBox_MouseUp(Button As...)
If Button = vbRightButton Then
TextBox.Enabled = False
Popupmenu MeinPopupMenu
TextBox.Enabled = True
TextBox.SetFocus
End If
End Sub
Diese Lösung funktioniert zwar offensichtlich ganz gut. Sie hat
aber einen entscheidenden Nachteil, der den Einsatz dieser Lösung
fast verbietet: Die TextBox verliert während dessen den Fokus. Das
hat aber zur Folge, dass das LostFocus-Ereignis der TextBox
ausgelöst wird, sobald es gesperrt wird. Ist (wie standardmäßig
eingestellt) die Eigenschaft CausesValidation der TextBox gesetzt,
wird auch noch das Validate-Ereignis ausgelöst. Sind dazu weitere
Steuerelemente auf dem Form vorhanden, erhält ein anderes dieser
Steuerelemente den Fokus - und dessen GotFocus-Ereignis wird
ausgelöst. Wird die Enabled-Eigenschaft der TextBox nach dem
Schließen des Popup-Menüs wieder auf True gesetzt, muss der Fokus
wieder auf die TextBox zurück gesetzt werden - die beschriebene
Ereignisreihenfolge wird im umgekehrten Sinne ausgelöst: Für das
Steuerelement, das zwischenzeitlich den Fokus erhalten hatte, wird
das LostFocus-Ereignis ausgelöst, gegebenenfalls dort auch das
Validate-Ereignis, und bei der TextBox nun das GotFocus-Ereignis.
Sie können sich bestimmt ganz leicht die Nebeneffekte
vorstellen, die hierbei Ihre Anwendung ganz schön ins Stolpern
bringen können, wenn Sie die betreffenden LostFocus- und
GotFocus-Ereignisse für ganz andere Zwecke zu ganz anderen
Zeitpunkten erwarten, und wenn Sie, was bei Datenbank-Forms wohl mit
Sicherheit der Fall sein dürfte, ausgiebig von Validate-Ereignissen
Gebrauch machen. Diese Lösung können Sie also nur ruhigen
Gewissens verwenden, wenn Sie die betroffenen Focus- und
Validate-Ereignisse keinesfalls verwenden werden. Ja, da steht:
"verwenden werden"! Denn falls Sie (oder jemand anderes)
später, nach dem Einsatz der beschriebenen Popup-Menü-Lösung,
doch noch diese Ereignisse verwenden und vielleicht nicht an die
damit verbundenen Komplikationen denken sollten, kommen Sie in
Teufels Küche...
Die zweite häufig vorgeschlagene Lösung setzt auf Subclassing -
auf das Abfangen der Nachricht WM_CONTEXTMENU. An und für sich ist
das die sauberste Lösung - sie hält sich an den vom System
vorgegebenen Mechanismus. Subclassing lässt sich zwar mit Visual
Basic und ein paar API-Techniken einrichten, ist aber eher eine
Angelegenheit für Fortgeschrittene. Wenn Sie diese Technik
beherrschen, wäre es die empfehlenswerteste Lösung. Sie können
auch eines der als Freeware, Shareware oder kommerziell
erhältlichen Subclassing-Controls verwenden. Falls Sie jedoch nur
wegen des Kontext-Menüs ein Subclassing für die TextBox einrichten
(ob mit VB-Code oder mit einem Subclassing-Control), wäre der
Aufwand gemessen am Nutzen sehr hoch (und bei VB-Code-Subclassing
die Gefahr von Instabilitäten und Abstürzen wäre ebenfalls recht
hoch).
So habe ich nach einer dritten Lösung gesucht - und sie
gefunden. Ich habe mich einfach einmal gefragt, ob man nicht das
MouseUp-Ereignis bei einem anderen Steuerelement ankommen lassen
könnte, statt bei der TextBox. Denn dann sollte - hoffentlich - das
eingebaute Kontext-Menü der TextBox nicht in die Quere kommen. Nach
nur kurzem Herumprobieren mit einer PictureBox stellte sich heraus,
dass das sogar recht einfach geht: Aus dem MouseDown-Ereignis der
TextBox heraus (wenn Button = vbRightButton) senden Sie
mit der API-Funktion SendMessage
die Nachricht WM_RBUTTONDOWN an eine PictureBox. Damit die TextBox
den Fokus nicht verliert, wird die PictureBox schon von vornherein
beim Entwurf unsichtbar gemacht (Visible = False) oder
gesperrt (Enabled = False). Beim Loslassen der rechten
Maustaste wird dann tatsächlich das MouseUp-Ereignis der PictureBox
ausgelöst. Setzen Sie noch vor dem Aufruf von SendMessage die Tag-Eigenschaft
der PictureBox auf den Namen des gewünschten Popup-Menüs, können
Sie im MouseUp-Ereignis der PictureBox dieses Menü wieder anhand
der Tag-Eigenschaft aus der Controls-Collection
herausfischen und anzeigen. Wie das nachfolgende (und
herunterladbare) Beispiel-Form mit zwei TextBoxen und zwei
verschiedenen Popup-Menüs zeigt, reicht sogar eine einzige
PictureBox auf einem Form für diese Lösung aus: Anhand der Tag-Eigenschaft
können Sie ja beliebig differenzieren und gegebenenfalls in weitere
Hilfsprozeduren verzweigen.
Private Declare Function SendMessage Lib "user32" _
Alias "SendMessageA" (ByVal hwnd As Long, ByVal wMsg As Long, _
ByVal wParam As Long, lParam As Any) As Long
Private Const WM_RBUTTONDOWN = &H204
Private Sub picPopupMenu_MouseUp(Button As Integer, _
Shift As Integer, X As Single, Y As Single)
On Error Resume Next
PopupMenu Me.Controls(picPopupMenu.Tag), vbPopupMenuRightButton
End Sub
Private Sub Text1_MouseDown(Button As Integer, _
Shift As Integer, X As Single, Y As Single)
If chkUserMenu1.Value Then
If Button = vbRightButton Then
With picPopupMenu
.Tag = "mnuText1"
SendMessage .hwnd, WM_RBUTTONDOWN, 0&, 0&
End With
End If
End If
End Sub
Private Sub Text2_MouseDown(Button As Integer, _
Shift As Integer, X As Single, Y As Single)
If chkUserMenu2.Value Then
If Button = vbRightButton Then
With picPopupMenu
.Tag = "mnuText2"
SendMessage .hwnd, WM_RBUTTONDOWN, 0&, 0&
End With
End If
End If
End Sub
Das funktioniert offensichtlich bei allen Steuerelementen, nicht
nur bei der TextBox, wenn Sie ein eingebautes Kontext-Menü
unterdrücken wollen (Natürlich gilt das ausschließlich unter der
Voraussetzung, dass sich das Steuerelement an die Konvention hält,
das Kontext-Menü erst beim Loslassen der rechten Maustaste
anzuzeigen). Und es funktioniert sogar bei Steuerelementen, bei
denen auch das Abfangen der WM_CONTEXTMENU-Nachricht per Subclassing
fruchtlos bleibt - zum Beispiel werden in Visual Basic geschriebene
UserControls wohl kaum mit dieser Nachricht dienen, sondern
wahrscheinlich ein Kontext-Menü einfach während eines internen
MouseUp-Ereignisses anzeigen.
|
|
|