Mausklick auf alle anderen Controls

  • VB.NET
  • .NET (FX) 4.0

Es gibt 9 Antworten in diesem Thema. Der letzte Beitrag () ist von drschef.

    Mausklick auf alle anderen Controls

    Ich habe eine Combobox 'gebastelt'. Da öffnet sich eine ListBox und schließt sich auch wieder, wenn ein Eintrag in der ListBox gewählt wurde. Die MS Combobox schließt sich aber auch, wenn auf ein beliebiges anderes Control geklickt wird. Nun kann man ja nicht Event handles an alle controls hängen.

    Wie kann man den Mausclick auf alle Controls gemeinsam ohne vielfache handles abfangen?
    fügst die Handler per AddHandler einer Sub hinzu


    Es sind leider ein paar viele Controls. Da wird das Hinzufügen der Handles eine Fleißarbeit und man muss bei jeder Ergänzung eines Controls daran denken, dass man das in die Reihe der Handles aufnimmt. Das erhöht nicht gerade die Sicherheit.

    Gibt es nicht so etwas wie ein neutrales Maus-Handle, was bestenfalls das Control zurückgibt, was angeklickt wurde. Es gibt doch auch die KeyPreview-Eigenschaft, bei der ein Tastaturereignis erst einmal in der Form ausgewertet wird, bevor der Key an das jeweilige Control weitergegeben wird.

    drschef schrieb:

    bei jeder Ergänzung eines Controls
    Du kannst ja in einer Schleife alle relevanten Controls abfragen und denen dann einen Eventhandler dranhängen.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    in einer Schleife alle relevanten Controls abfragen


    Danke. Geht zur Not. Aber es sind auch einige Container dabei. Da wird gleich eine Hierarchie draus. Ich hatte gehofft, wie oben beschrieben, dass man das Maus-Ereignis irgendwie neutral auswerten kann. Windows muss es doch auch so abwickeln. Bevor es feststellt, welches Objekt von der Maus-Aktion betroffen ist, stellt es doch erst mal fest, welches Fenster betroffen ist. Und wie gesagt, bei den Tastencodes geht es doch auch.

    Ich habe übrigens im Moment folgende Lösung: Die ListBox kriegt den Focus. Dann nimmt jede Aktion auf Label oder TextBox den Focus weg und mit dem LostFocus für die Listbox wird sie zugemacht. Außerdem eine HandlesReihe wie

    Quellcode

    1. Private Sub MyMouseClick(sender As Object, e As System.Windows.Forms.MouseEventArgs) Handles Me.MouseClick, Gruppe1.MouseClick, Gruppe2.MouseClick, Gruppe3.MouseClick, Box2D.MouseClick, Box3D.MouseClick, BoxSB.MouseClick, BoxUT.MouseClick, Bild.MouseClick, PB.MouseClick, Bildleiste.MouseClick


    Damit wird immer noch nicht jedes Gekrümel erschlagen, aber es geht zur Not. Ich hätte mir etwas Kompakteres und Eleganteres gewünscht, was auch nicht für jedes hinzugefügte Control wieder eine Aktion erfordert.
    Wie Rod schon gesagt hat, Lauf einfach alle Controls per Schleife durch. Wenn es noch mit Children funktionieren soll, geh die Controls rekursiv durch und füg mittels AddHandler deinen Handler hinzu. Soweit ich weiß, gibt es noch ein ControlAdded-Event, eventuell ließe sich auch darüber was regeln.

    drschef schrieb:

    irgendwie neutral auswerten
    Klar kannst Du das in der WndProc machen. Hole Dir zum Mausklick das Fensterhandle und dann mit diesem alles weitere.
    Ich denke mal, der Gesamtaufwand bleibt konstant. Fragt sich, was einfacher zu pflegen ist.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Hole Dir zum Mausklick das Fensterhandle und dann mit diesem alles weitere.


    Klingt gut, weil ich dann nur noch fragen brauche, ob die Mausklick-Koordinate außerhalb der Listbox liegt und könnte dann die Listbox schließen.

    Aber bisher habe ich erfolglos an der hwnd-Geschichte herum gebastelt. Hier mal der Kern, der aber leider einen Fehler bringt:

    Quellcode

    1. Private Declare Function GetCursorPos Lib "user32" (lpPoint As Pointapi) As Long
    2. Private Declare Function ScreenToClient Lib "user32" (ByVal hwnd As Long, lpPoint As Pointapi) As Long
    3. Dim pt As Pointapi
    4. Private Structure Pointapi
    5. Dim x As Integer
    6. Dim y As Integer
    7. End Structure
    8. Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
    9. GetCursorPos(pt)
    10. ScreenToClient(CLng(Me.Handle), pt)
    11. MessageBox.Show("X: " & pt.x & " Y: " & pt.y)
    12. End Sub


    Bei GetCursorPos kommt Fehlermeldung "PlnvokeStackImbalanz wurde erkannt". Damit kann ich nichts anfangen.

    drschef schrieb:

    PlnvokeStackImbalanz
    Du hast Ranz-VB6-Deklarationen verwendet.
    Gugst Du hier.
    Die Cursor-Position holst Du Dir so:

    VB.NET-Quellcode

    1. Dim point = Cursor.Position
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Ich habe folgende Lösung realisiert:

    Quellcode

    1. Private Sub LBox_SelectedIndexChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles LBox.SelectedIndexChanged
    2. '...
    3. LBox.Visible = False
    4. End Sub
    5. Private Sub LBox_LostFocus(ByVal sender As Object, ByVal e As System.EventArgs) Handles LBox.LostFocus
    6. LBox.Visible = False
    7. End Sub
    8. Private Sub AllObjectsMouseDown(ByVal TopCtl As Control)
    9. For Each CTL As Control In TopCtl.Controls
    10. If CTL.Controls.Count > 0 Then
    11. AllObjectsMouseDown(CTL)
    12. End If
    13. 'Für alle Toolstrips
    14. If CTL.GetType().Equals(GetType(ToolStrip)) Then
    15. For Each item As ToolStripItem In DirectCast(CTL, ToolStrip).Items
    16. If item.Name <> "" And item.Name <> "LButton" Then
    17. AddHandler item.MouseDown, AddressOf LBox_Schließen
    18. End If
    19. Next
    20. End If
    21. If CTL.Name <> "" And CTL.Name <> "LButton" Then
    22. AddHandler CTL.MouseDown, AddressOf LBox_Schließen
    23. End If
    24. Next
    25. For Each ITM As ToolStripMenuItem In MenuStrip1.Items()
    26. If ITM.Name <> "" Then
    27. AddHandler ITM.MouseDown, AddressOf LBox_Schließen
    28. End If
    29. Next
    30. AddHandler Me.MouseDown, AddressOf LBox_Schließen
    31. End Sub
    32. Private Sub LBox_Schließen(sender As Object, e As System.Windows.Forms.MouseEventArgs)
    33. LBox.Visible = False
    34. End Sub


    Die 'AllObjectsMouseDown' muss in die Load-Prozedur rein.
    Sie realisiert das Schließen der Listbox bei allen möglichen MouseDown-Events zufriedenstellend und einigermaßen allgemein. Man muss nur darauf achten, dass das MouseDown-Event nicht noch irgendwo anderweitig verwendet wird.

    Leider bin ich bei der alternativen Variante mit den APIs bisher noch auf keinen grünen Zweig gekommen. Der Hinweis von Rod hat mich zwar ein wenig weiter gebracht. Aber den Fehler, dass die Übergabeparameter von ScreenToClient nicht passen, habe ich noch nicht heraus gekriegt.

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „drschef“ ()