Controls: Verschieben mittels Drag & Drop und das Click Problem

  • VB.NET

Es gibt 7 Antworten in diesem Thema. Der letzte Beitrag () ist von ErfinderDesRades.

    Controls: Verschieben mittels Drag & Drop und das Click Problem

    Guten Tag,

    ich entwickle gerade einen Formular Editor für ein größeres Projekt.
    Der Formular Editor lässt sich dynamaisch und bequem in jedes Formular einbetten, speichert die Positionen, Funktionen und so weiter.

    Mit aktivieren des Formular Editors habe ich nun die Möglichkeit diverse Steuerelemente/Controls zu markieren (über klick/focus) und ich kann diese Steuerelemente auch per Drag & Drop auf dem Formular verschieben, auf anderen Panels/Groupbox verschieben und so weiter.

    Die Funktionsweise ist gegeben und ich bin mit dem Ergebnis soweit ganz gut zufrieden.

    Das Problem:
    Schwierig wird es allerdings bei einem Punkt.
    In dem Moment wo der Benutzer ein Steuerelement - sei es ein Button oder eine Textbox - auswählt und diese markiert wird passiert es oft das direkt der Drop beim Drag & Drop ausgeführt wird.
    Resultat ist dann das sich das Steuerelement ein wenig verschoben hat anstatt es einfach nur zu markieren.
    Und hier bräuchte ich mal Hilfe.

    Das ganze sieht ungefähr wie folgt aus....
    (Die Routine ist um unwichtige Punkte abgespeckt worden)

    VB.NET-Quellcode

    1. Public Sub Control_DragDrop(ByVal sender As Object, ByVal e As System.Windows.Forms.DragEventArgs)
    2. Try
    3. Dim TargetPanel As Control = DirectCast(sender, Control)
    4. Dim ctrl As New Control
    5. Dim iLocation As New System.Drawing.Point
    6. If e.Data.GetDataPresent(GetType(Button)) Then
    7. Dim tCtrl As Button = DirectCast(e.Data.GetData(GetType(Button)), Button)
    8. ctrl = DirectCast(tCtrl, Control)
    9. End If
    10. Dim ParentPanel As Control = ctrl.Parent
    11. If TypeOf (TargetPanel) Is GroupBox Then
    12. iLocation = CType(sender, GroupBox).PointToClient(Cursor.Position)
    13. End If
    14. If TypeOf (sender) Is Form Then
    15. iLocation = CType(sender, Form).PointToClient(Cursor.Position)
    16. End If
    17. If Not TargetPanel Is ParentPanel And Not TargetPanel Is ctrl Then
    18. TargetPanel.Controls.Add(ctrl)
    19. ctrl.Location = iLocation
    20. ParentPanel.Controls.Remove(ctrl)
    21. RemoveHandler ParentPanel.Paint, AddressOf gPaint
    22. ParentPanel.Refresh()
    23. Treeview_Control_Fill(gForm, tvControls)
    24. Else
    25. ctrl.Location = iLocation
    26. End If
    27. ctrl.Refresh()
    28. If Not ctrl.Parent Is Nothing Then
    29. ctrl.Parent.Refresh()
    30. End If
    31. Catch ex As Exception
    32. Debug_Error(System.Reflection.MethodBase.GetCurrentMethod().Name, ex.Message, main.Settings.iDebugMessage)
    33. End Try
    34. End Sub


    Die Routine, welche den DragDrop auslöst

    VB.NET-Quellcode

    1. Public Sub Control_MouseMove(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs)
    2. Try
    3. If e.Button = MouseButtons.Left Then
    4. Dim ctrl As Control = CType(sender, Control)
    5. If IsUsercontrol(ctrl) = True Then 'Überprüft ob dieses Control auch verschoben werden darf
    6. ctrl.DoDragDrop(ctrl, DragDropEffects.Move)
    7. End If
    8. End If
    9. Catch ex As Exception
    10. Debug_Error(System.Reflection.MethodBase.GetCurrentMethod().Name, ex.Message, main.Settings.iDebugMessage)
    11. End Try
    12. End Sub



    Ich glaube/hoffe damit schon mal alle relevanten Dinge aufgezeigt zu haben.
    Das ganze ist natürlich sehr komplex da es am Ende einem Form-Designer "ähneln" soll und eben zur Laufzeit funktioniert.

    Über jegliche Hilfe und Ansätze, auch über Vorschläge wie man etwas eleganter lösen kann, wäre ich sehr Dankbar.
    wäre das mouseklick event zum auswählen nicht besser geeignet?
    ansonsten könntest du die vorherige mausposition speichern und mit der aktuellen vergleichen. ist das ganze weniger als 4 pixel verschoben (so ist es glaubich bei windows) zählt das ganze als "klick"
    Hi,
    danke das du dich gerade meinem Problem annimmst.

    FreakJNS schrieb:

    wäre das mouseklick event zum auswählen nicht besser geeignet?

    Da war ich eh am rum probieren, habe das MouseClick Ereignis auch für das erstellen neuer Steuerelemente genutzt.
    Die Auswahl darüber ebenfalls.


    FreakJNS schrieb:


    ansonsten könntest du die vorherige mausposition speichern und mit der aktuellen vergleichen. ist das ganze weniger als 4 pixel verschoben (so ist es glaubich bei windows) zählt das ganze als "klick"

    An diese Idee habe ich auch gedacht aber das, was ich da probiert hatte, hat vorne und hinten mit den Positionen nicht hin gehauen.
    Hättest du da vielleicht ein Beispiel-Code?
    Ich nutze MDI Formulare.

    Danke
    hiermal das Sample von Controls draggen modifiziert:

    VB.NET-Quellcode

    1. Public Class Form1
    2. Private _pDown As New Point(Short.MinValue, Short.MinValue)
    3. Private _GrabOffs As Size
    4. Private _PBoxes As PictureBox()
    5. Private Sub Control_MouseDown(ByVal sender As Object, ByVal e As MouseEventArgs)
    6. 'funzt mit allen Controls
    7. Dim toDrag = DirectCast(sender, Control)
    8. _GrabOffs = New Size(Control.MousePosition) - New Size(toDrag.Location)
    9. _pDown = Control.MousePosition - _GrabOffs
    10. 'toDrag.Parent.Controls.SetChildIndex(toDrag, 0)
    11. End Sub
    12. Private Sub Control_MousUp(ByVal sender As Object, ByVal e As MouseEventArgs)
    13. _pDown = New Point(Short.MinValue, Short.MinValue)
    14. End Sub
    15. Private Sub Control_MouseMove(ByVal sender As Object, ByVal e As MouseEventArgs)
    16. 'funzt mit allen Controls
    17. If e.Button <> MouseButtons.Left Then Return
    18. Dim newLoc = Control.MousePosition - _GrabOffs
    19. Const tolerance = 20
    20. Dim rct = New Rectangle(newLoc, New Size(2 * tolerance, 2 * tolerance))
    21. rct.Offset(-tolerance, -tolerance)
    22. If rct.Contains(_pDown) Then Return
    23. _pDown = New Point(Short.MinValue, Short.MinValue)
    24. Dim toDrag = DirectCast(sender, Control)
    25. RemoveHandler toDrag.MouseMove, AddressOf Control_MouseMove
    26. toDrag.Location = newLoc
    27. AddHandler toDrag.MouseMove, AddressOf Control_MouseMove
    28. End Sub
    29. Private Sub Form_Load(ByVal sender As Object, ByVal e As EventArgs) Handles MyBase.Load
    30. _PBoxes = Me.Controls.OfType(Of PictureBox)().ToArray
    31. For Each pb In _PBoxes
    32. AddHandler pb.MouseDown, AddressOf Control_MouseDown
    33. AddHandler pb.MouseMove, AddressOf Control_MouseMove
    34. AddHandler pb.MouseUp, AddressOf Control_MousUp
    35. Next
    36. End Sub
    37. End Class


    ups - erledigt?
    Hi,

    in der Tat - das war sehr Hilfreich und durch etwas Anpassung konnte ich es in mein Projekt implementieren.

    Dein Beispiel bezieht sich lediglich darauf etwas innerhalb eines Panels/Forms zu verschieben und deshalb habe ich an besagter Stelle das DragDrop Ereignis zusätzlich ausführen lassen.
    Denn das Steuerelement muss ja auch weiterhin auf ein anderes Steuerlement - wie GroupBox oder Panel - gezogen werden können. Und lediglich beim DragDrop Ereignis bekomme ich auch das Ziel-Steuerelement ausgewählt.

    Angepasst sieht es wie folgt aus.

    VB.NET-Quellcode

    1. Public Sub Control_MouseMove(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs)
    2. Try
    3. If e.Button = MouseButtons.Left Then
    4. Dim newLoc As Drawing.Point = Control.MousePosition - _GrabOffs
    5. newLoc = Control.MousePosition - _GrabOffs
    6. Const tolerance = 20
    7. Dim rct = New Rectangle(newLoc, New Size(2 * tolerance, 2 * tolerance))
    8. rct.Offset(-tolerance, -tolerance)
    9. If rct.Contains(_pDown) Then Return
    10. _pDown = New Point(Short.MinValue, Short.MinValue)
    11. Dim toDrag = DirectCast(sender, Control)
    12. RemoveHandler toDrag.MouseMove, AddressOf Control_MouseMove
    13. toDrag.Location = newLoc
    14. Dim ctrl As Control = CType(sender, Control)
    15. If IsUsercontrol(ctrl) = True Then
    16. 'Das Drag & Drop Ereignis starten
    17. ctrl.DoDragDrop(ctrl, DragDropEffects.Move)
    18. End If
    19. AddHandler toDrag.MouseMove, AddressOf Control_MouseMove
    20. End If
    21. Catch ex As Exception
    22. Debug_Error(System.Reflection.MethodBase.GetCurrentMethod().Name, ex.Message, main.Settings.iDebugMessage)
    23. End Try
    24. End Sub



    Ich bedanke mich vielmals.
    Mein Problem hat es damit nämlich gelöst.

    P.S.
    Ich war nun ein wenig verwundert darüber das mein Thema als schon "Erledigt" markiert ist. Dabei sollte das eigentlich in meinem Ermessen als erledigt markiert werden, hätte ja sein können das ich manches am Quellcode nicht verstehe oder in dem meinigen Implementieren kann ;)
    du erleichterst dir das Debuggen (und damit das Coden überhaupt) sehr, wennde den TryCatch wegmachst - gugge AvoidTryCatch

    Jo - eiglich kannst nur du selbst auf den Erledigt-Button klicksen - wirstewohl versehentlich draufgekommen sein.

    deine ctrl-Variable ist üflüssig. Denselben Wert erfasse ich nämlich schon in der Variable toDrag.

    Äh - tatsächlich ist das meiste von meinem Code allerdings selbst üflüssig, denn wenn du mit Control.DoDragDrop draggst, dann bewegt sich während des Draggens ja nicht das ganze Control ( wie bei mir ), sondern doch nur das Drag-Symbol - oder?

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

    Hallo,

    ErfinderDesRades schrieb:

    du erleichterst dir das Debuggen (und damit das Coden überhaupt) sehr, wennde den TryCatch wegmachst - gugge AvoidTryCatch

    danke für den Tipp, das werde ich mir doch einmal genauer anschauen.
    Jedoch lasse ich mir diverse Programm-Fehler loggen und für bestimmte User(in dem Falle mich) ausgeben damit der Benutzer nichts davon mitbekommen muss, ich aber sehe was, wann und wo passiert ist.


    ErfinderDesRades schrieb:


    Jo - eiglich kannst nur du selbst auf den Erledigt-Button klicksen - wirstewohl versehentlich draufgekommen sein.

    Oh, ich dachte das wäre jemand anders gewesen. Entschuldige.


    ErfinderDesRades schrieb:


    deine ctrl-Variable ist üflüssig. Denselben Wert erfasse ich nämlich schon in der Variable toDrag.

    Äh - tatsächlich ist das meiste von meinem Code allerdings selbst üflüssig, denn wenn du mit Control.DoDragDrop draggst, dann bewegt sich während des Draggens ja nicht das ganze Control ( wie bei mir ), sondern doch nur das Drag-Symbol - oder?

    Da hast du recht, die Visualisierung verschwindet dadurch direkt.
    Ich muss dazu aber auch sagen, das ich erst mal den Quellcode von dir übernommen und angepasst habe.
    Als es dann funktioniert hat wollte ich schnell eben verdeutlichen, das ich das Problem lösen konnte.
    Im nach hinein habe ich das natürlich angepasst :)

    Wie so oft kann ich nur Danke sagen :)

    Bibi schrieb:

    Jedoch lasse ich mir diverse Programm-Fehler loggen und für bestimmte User(in dem Falle mich) ausgeben damit der Benutzer nichts davon mitbekommen muss, ich aber sehe was, wann und wo passiert ist.

    Solange du am Entwickeln bist (und das ist wohl 99% der Zeit, die du mit dem Prog zu tun hast), ists doch vielfach günstiger, die Fehler direkt vor die Nase zu kriegen, mit CodeStop, LokalFenster, Aufrufeliste und dem ganzen Pipapo. Dass du den Fehler behebst, sodass er nicht mehr auftritt.

    Son Error-Logging kann man auch ganz allgemein einbauen, im Application.UnhandledException-Event. Kann man sogar so deichseln, dasses nur für die Release-Version anspringt - ein Beispiel ist im Code von AsyncWorker - CodeProject.

    Allerdings wird die App damit beendet - was ja eh üblicherweise sicherste ist, wenn wirkliche Fehler auftreten.

    Eine wirkliche Fehlerbehandlung ist ja logisch überhaupt erst dann möglich, wenn die App fertig ist, und wenn der Fehler ein erwarteter Fehler ist, für den du dann auch wirklich eine Lösung auf der Pfanne hast, wie die App in einem stabilen Zustand weiterlaufen kann.

    Wenn etwa bei deiner Draggerei der User da irgendeinen Mist hindraggt, der das Teil zum kippen bringt - was willst du denn da machen? Ihm nix sagen, und dir heimlich eine Log-Mail senden? Na der wird sich ja wundern, wenner am nächsten Tag mit seinen Ergebnisse weiter-arbeiten will, und die können garnetmehr geladen werden, oder so Scherze.

    Grade Draggen ist von MS katastrophal fehldesigned, denn im Drop-Event (und schon im DragOver) funktioniert die Fehlermelderei nicht mehr.
    Ich hab einen Workaround dazu veröffentlicht, aber der ist ätsch! in c#: Draggen in der Anwendung