Maus verliert Kontakt zu Scrollbalken

  • VB.NET
  • .NET (FX) 4.0

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

    Maus verliert Kontakt zu Scrollbalken

    Hallo,

    ich verwende ein Panel mit der Eigenschaft Autoscroll=true zur Verwaltung einer Kollektion von Zellen mit Pictureboxen. Beim Scrollen des Panels wird der Inhalt fortlaufend umgewälzt, um eine große Kollektion im Ausschnitt anzeigen zu können. Der angezeigte Ausschnitt folgt vorwärts wie rückwärts der Scroll-Position.

    Dabei ergibt sich ein unschöner Effekt, der wohl eher mit dem Scrollbalken und den Mauskoordinaten zu tun hat, als mit meinem Algorithmus: Wenn man die Maus beim Scrollen vom Scrollbalken weg bewegt, funktioniert die Kopplung auch bis zu einer Entfernung von wenigen Zentimetern vom Balken nach links und rechts. Wird der Abstand der Maus vom Balken größer als etwa 5 cm, springt der Scrollbuttton an den Anfang, kehrt aber wieder an die alte Position zurück, wenn sich die Maus dem Balken wieder nähert. Im Internet bin ich auf den Beitrag Scrollbalken springt, wenn man die Maus wegbewegt gestoßen, der darauf hindeutet, dass das Verhalten an vielen Stellen beobachtet und als störend empfunden wurde unter anderem findet es sich auch beim Windows-Explorer.

    Sinnvoll wäre es, zu erzwingen, dass die Maus das Rechteck des Scrollbalkens gar nicht erst verlässt. Aber Wie kann man die aktuellen Mauskoordinaten so korrigieren, dass der Scrollbalken die Maus an sich bindet? Man könnte das Problem auch anders angehen, indem man das Eintreten des Kontaktverlusts irgendwie registriert und dann einfach den Scrollinhalt fixiert. Da tritt aber eine aufwendig behandelbare Nebenwirkung ein, wenn die Maus nicht an der gleichen Stelle wieder in den aktiven Bereich eintritt.
    @drschef Da musst Du einfach die ScrollBar die Maus capturen lassen und feddich.
    docs.microsoft.com/en-us/dotne…-capture-in-windows-forms
    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!
    Hallo,

    danke für die schnelle Antwort. Wie kann die Scrollbar die Maus capturen? Die Scrollbar ist kein selbständiges Control.
    Entschuldigung, da war ja noch der Link. Den muss ich erst mal anschauen. Nach Aufsuchen des Links bin ich noch nicht viel schlauer, weil wie gesagt, die Scrollbar kein selbständiges Control ist.

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

    @drschef Wenn das Panel nicht zu groß ist, capture die Maus auf dem ganzen Panel.
    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!
    @drschef Ja und?
    Dann gehst Du sukzessive ein Control weiter nach oben, bis Du letztenendes bei der Form angekommen bist. :thumbsup:
    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!
    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!
    Danke, habe begonnen auszuprobieren, aber schon die beiden Zeilen

    VB.NET-Quellcode

    1. Dim hwnd As IntPtr = Me.Handle
    2. SetCapture(hwnd)


    führen zu "Option Strict On" lässt keine impliziten Konvertierungen von IntPrt in Long zu. Mit

    VB.NET-Quellcode

    1. SetCapture(CLng(hwnd))


    kommt PinvokeStackImbalance

    Liegt vielleicht an meiner geringen Vertrautheit mit API-Funktionen.

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

    Hallo.

    Vielleicht ist meine letzte Antwort oder besser Frage zur Verwendung von SetCapture übersehen worden, weil sie von mir noch einmal nachträglich editiert wurde.

    Ansonsten bin ich nach längerem Experimentieren am Zweifeln, ob es überhaupt einen Weg gibt, das Ausgangsproblem zu lösen. Mein Eindruck ist, dass der Autoscroll-Balken die meisten Einflussmöglichkeiten abblockt. Sonst hätte sich ja auch das kuriose Verhalten beim Windows-Explorer vermeiden lassen. Wenn die Maus den Kontakt zum Rollbalken verliert, springt der Rollbalken an den Startpunkt und wenn er ihn wieder gewinnt, springt er wieder an die alte Stelle. Warum bleibt die Anzeige nicht einfach dort wo sie war und meldet einen Fehler. Genau das schwebt mir nämlich vor, weil es nutzerfreundlicher ist.

    Oder findet sich doch noch eine Lösung? Habe ich irgend ein Event übersehen, welches den Kontaktverlust zwischen Maus und Rollbalken und zurück meldet?

    drschef schrieb:

    kommt PinvokeStackImbalance
    Lesen bildet:

    RodFromGermany schrieb:

    Dann gehen wir halt über die API:
    SetCapture(hWnd As IntPtr)

    ====
    Du kannst natürlich ein eigenes ScrollablePanel bauen und dort mit eigenen ScrollBars hantieren, deren Handle Du dann explizit inn der Hand hast und mit diesem die Maus capturen kannst.
    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!
    Hallo,

    danke für die schnelle Reaktion. Ein Panel mit eigenen Scrollbars erwäge ich vorerst nicht. Da müsste ich den schon verwendbaren Teil ersetzen und der funktioniert eigentlich sehr gut. Lieber könnte ich mich dazu entschließen, mit diesem Schönheitsfehler beim Autoscroll zu leben, so wie es Microsoft ja schon tut. Es spart viel Arbeit und bisher ist die Macke ja wohl kaum jemand aufgefallen, obwohl jeder den Windows-Explorer täglich nutzt.

    Sollte ich bei meinen Spielereien doch noch eine Möglichkeit finden, den Grenzübertritt wenigstens mit einer Fehler-Message zu begleiten, melde ich mich wieder.
    Nachtrag und danke für RodFromGermany

    Falls es Dich interessiert: Das Programm, um welches es hier geht, ist ziemlich komplex geworden, so dass ich ungern nochmal Teile davon einreise. Falls Du das hier anstehende Problem mit dem internen Explorer selbst nachvollziehen möchtest, ist hier der Link: drschef.de/fivx. Aber allmählich gewöhne ich mich an den Gedanken mit dem kleinen Lapsus zu leben.
    @drschef Kannst Du mal diese Form (.vb, .designer.vb und .resx) als ZIP posten?
    Ich will mal was probieren.
    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!
    @drschef Zwischendurch mal eine schlechte Nachricht:
    Die Scrollbars reagieren nicht auf MouseDown & Co. im Panel, Capture greift da nicht.
    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!
    Die Scrollbars reagieren nicht auf MouseDown & Co. im Panel, Capture greift da nicht.

    Genau das ist das Problem. Es gibt ja aber auch kein Event was den Austritt aus dem Scroll-Ereignis anzeigt. Ich habe im Anhang die Form als Archiv.zip. Wird aber nicht ganz einfach, weil das Ding an tausend Stellen mit der Registry, anderen Klassen oder Modulen verzahnt ist.

    Ist vmtl ein Feature und kein Bug. Ich finds auf jeden Fall nützlich. So hat man nämlich die Möglichkeit auf die Startposition zurück zu kommen.

    Ja, hat einen Nutzen, wenn auch gering. Auf die Startposition kommt man auch so. Öfters wird wahrscheinlich jemand überrascht drein blicken, wenn plötzlich sein Viewport zusammen bricht oder einem ganz anderen Platz macht. Lies mal den Beitrag Scrollbalken springt, wenn man die Maus wegbewegt.
    Dateien
    • Archiv.zip

      (23,22 kB, 77 mal heruntergeladen, zuletzt: )
    @drschef Ich hab mal ein wenig geschmökert.
    Das ist ne Scrollbar, die genau das macht, was Du willst.
    Teste sie in einem eigenen Projekt.
    Eigene Scrollbars
    Kann sein, dass sie rumspinnt, hier die Überarbeitung:
    Spoiler anzeigen

    VB.NET-Quellcode

    1. Imports System.Drawing.Drawing2D
    2. Imports System.Drawing.Text
    3. Public Class CustomVScrollBar
    4. Inherits Control
    5. Public Enum ColorScheme
    6. Dark
    7. Bright
    8. End Enum
    9. Dim Thumb As Rectangle
    10. Dim _ColorScheme As ColorScheme
    11. Dim _thumby As Integer
    12. Dim _vis As Double = 0.1
    13. Dim _thumbsize As Integer = 20
    14. Dim _val As Double
    15. Dim Pressed As Boolean = False
    16. Public Event Scroll()
    17. Property VisibleRatio As Double
    18. Get
    19. Return _vis
    20. End Get
    21. Set(ByVal value As Double)
    22. _vis = value
    23. _thumbsize = CInt(Height * _vis)
    24. Invalidate()
    25. End Set
    26. End Property
    27. Property Value As Double
    28. Get
    29. Return _val
    30. End Get
    31. Set(ByVal value As Double)
    32. _val = value
    33. Invalidate()
    34. RaiseEvent Scroll()
    35. End Set
    36. End Property
    37. Public Property ColorSchemeX As ColorScheme
    38. Get
    39. Return _ColorScheme
    40. End Get
    41. Set(value As ColorScheme)
    42. _ColorScheme = value
    43. Invalidate()
    44. End Set
    45. End Property
    46. Protected Overrides Sub OnMouseDown(ByVal e As MouseEventArgs)
    47. If e.Button = MouseButtons.Left Then
    48. If Thumb.Contains(e.Location) Then
    49. Pressed = True
    50. Else
    51. If e.Y < Thumb.Y Then _thumby = CInt(_val - 10) Else _thumby = CInt(_val + 10)
    52. _val = Math.Min(Math.Max(_thumby, 0), 100)
    53. Invalidate()
    54. RaiseEvent Scroll()
    55. End If
    56. End If
    57. End Sub
    58. Protected Overrides Sub OnMouseUp(ByVal e As MouseEventArgs)
    59. Pressed = False
    60. End Sub
    61. Protected Overrides Sub OnMouseMove(ByVal e As MouseEventArgs)
    62. If Pressed Then
    63. Dim ThumbPosition As Integer = CInt(e.Y - (_thumbsize / 2))
    64. Dim ThumbBounds As Integer = Height - _thumbsize
    65. _thumby = CInt((ThumbPosition / ThumbBounds) * 100)
    66. _val = Math.Min(Math.Max(_thumby, 0), 100)
    67. Invalidate()
    68. RaiseEvent Scroll()
    69. End If
    70. End Sub
    71. Sub New()
    72. SetStyle(ControlStyles.OptimizedDoubleBuffer Or ControlStyles.AllPaintingInWmPaint Or ControlStyles.ResizeRedraw Or ControlStyles.UserPaint Or ControlStyles.Selectable Or ControlStyles.SupportsTransparentBackColor, True)
    73. DoubleBuffered = True
    74. End Sub
    75. Protected Overrides Sub OnPaint(e As System.Windows.Forms.PaintEventArgs)
    76. Thumb = New Rectangle(0, 0, Width, _thumbsize)
    77. Thumb.Y = CInt(((_val) / 100) * (Height - _thumbsize))
    78. Dim _BaseColor As Color
    79. Dim _ThumbColor As Color
    80. If _ColorScheme = ColorScheme.Dark Then
    81. _BaseColor = Color.FromArgb(64, 64, 64)
    82. _ThumbColor = Color.FromArgb(70, 70, 70)
    83. Else
    84. _BaseColor = Color.White
    85. _ThumbColor = Color.WhiteSmoke
    86. End If
    87. If Not VisibleRatio >= 1 Then
    88. Dim g = e.Graphics
    89. With g
    90. .TextRenderingHint = TextRenderingHint.ClearTypeGridFit
    91. .SmoothingMode = SmoothingMode.HighQuality
    92. .PixelOffsetMode = PixelOffsetMode.HighQuality
    93. .Clear(_BaseColor)
    94. .FillRectangle(New SolidBrush(_ThumbColor), Thumb)
    95. .DrawLine(New Pen(Color.SteelBlue, 2), New Point(CInt(Thumb.Width / 2), Thumb.Y + 4), New Point(CInt(Thumb.Width / 2), Thumb.Bottom - 4))
    96. .InterpolationMode = InterpolationMode.HighQualityBicubic
    97. End With
    98. End If
    99. End Sub
    100. End Class
    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!