Einfärben VSCROLL funktioniert nicht

  • VB.NET

Es gibt 16 Antworten in diesem Thema. Der letzte Beitrag () ist von Peter329.

    Einfärben VSCROLL funktioniert nicht

    Hi,

    eigentlich dachte ich, dass Thema hätte ich erledigt:

    Es geht darum die Hintergrundfarbe eines Schiebereglers einzufärben, damit er besser lesbar ist. So habe ich das mit eurer Hilfe gelöst:

    VB.NET-Quellcode

    1. Imports System.Drawing.Imaging
    2. Imports System.Runtime.InteropServices
    3. Public Class Form1
    4. Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    5. ' GDI Brush von einer Farbe erstellen
    6. hBrush = CreateSolidBrush(&H4010&)
    7. Bmp = New Bitmap(4, 4, PixelFormat.Format24bppRgb)
    8. Using G As Graphics = Graphics.FromImage(Bmp)
    9. G.FillRectangle(Brushes.Yellow, 0, 0, 2, 2)
    10. G.FillRectangle(Brushes.Red, 2, 2, 2, 2)
    11. End Using
    12. hBmp = Bmp.GetHbitmap
    13. hBmpBrush = CreatePatternBrush(hBmp)
    14. End Sub
    15. 'Background color SCROLLBAR
    16. Protected Overrides Sub WndProc(ByRef m As Message)
    17. If m.Msg = WM_CTLCOLORSCROLLBAR Then
    18. If hBrush = IntPtr.Zero Then
    19. Return
    20. End If
    21. Dim t As Type = Control.FromHandle(m.LParam).GetType()
    22. Select Case t
    23. Case GetType(HScrollBar)
    24. m.Result = hBrush
    25. Case GetType(VScrollBar)
    26. m.Result = hBrush
    27. End Select
    28. Else
    29. MyBase.WndProc(m)
    30. End If
    31. End Sub
    32. Public Const WM_CTLCOLORSCROLLBAR = &H137
    33. Public hBmp As IntPtr = IntPtr.Zero
    34. Public hBrush As IntPtr = IntPtr.Zero
    35. Public hBmpBrush As IntPtr = IntPtr.Zero
    36. Public Bmp As Bitmap = Nothing
    37. <DllImport("gdi32.dll", EntryPoint:="CreatePatternBrush")>
    38. Public Shared Function CreatePatternBrush(
    39. ByVal hBitmap As IntPtr) As IntPtr
    40. End Function
    41. <DllImport("gdi32.dll", EntryPoint:="CreateSolidBrush")>
    42. Public Shared Function CreateSolidBrush(ByVal crColor As Integer) As IntPtr
    43. End Function
    44. <DllImport("gdi32.dll", EntryPoint:="DeleteObject")>
    45. Public Shared Function DeleteObject(ByVal hObject As IntPtr) As Integer
    46. End Function
    47. End Class


    Das funktioniert auch wunderbar ... es sei denn der Schieberegler ist in einem Panel eines SplitContainers enthalten. Im angehängten Screenshot sieht man das Problem: der linke Schieberegler wird wie gewünscht eingefärbt, der rechte Schieberegler aber bleibt wie er ist, weil er im Panel2 eines SplitContainers liegt.

    Ich hab das versucht zu tracen: Die WndProc feuert schlicht und ergreifend nicht für den zweiten Schieberegler. Und deshalb wird er auch nicht eingefärbt.

    Hat jemand eine Idee, wie ich erreichen kann, dass für den zweiten Schieberegeler die Messsage WM_CTLCOLORSCROLLBAR gefeuert wird ?

    Ich hoffe, ich habe mein Problem verständlich machen können.

    LG
    Peter
    Bilder
    • s 2022-09-25 08-13-125.jpg

      11,53 kB, 397×386, 40 mal angesehen
    @WndProc Probierma:
    Leite mal SplitContainer in einer eigenen Klasse ab und gib der eine eigene Prozedur WndProc.
    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!
    Gute Idee .... das hab ich gleich mal ausprobiert:

    VB.NET-Quellcode

    1. Friend Class mySplitcontainer : Inherits SplitContainer
    2. 'Background color SCROLLBAR
    3. Protected Overrides Sub WndProc(ByRef m As Message)
    4. ...


    Das gibt zwar keinen Fehler ... aber die Scrollbar bleibt immer noch so wie sie ist. Die neue WndProc feuert zwar aber es gibt keine Message WM_CTLCOLORSCROLLBAR. (s. Anhang)

    Mache ich irgend etwas falsch ?
    Bilder
    • s 2022-09-25 09-42-259.jpg

      11,98 kB, 441×535, 35 mal angesehen
    Ich glaube das Problem ist, das der SplitContainer ja Panels hat. SplitContainer.Panel1 und SplitContainer.Panel2.

    Peter ich rate dir mal WPF anzuschauen, mit XAML geht das viel leichter, da hat man solche Probleme nicht. Hast du da einmal einen Style für alle ScrollBars festgelegt, sehen auch alle so aus. Vergiss WinForms.
    @BitBrösel

    Das Problem ist, dass es sich um eine fertige Anwendung handelt ... die ist ziemlich umfangreich - und die würde ich nicht gar so gern neu schreiben, nur weil ich den Schieberegler einfärben will ... Im dümmsten Fall muss ich den Split Container halt entfernen .... das wäre allerdings auch blöde, weil ich dann die Collapse Logik "händisch" nachvollziehen müsste.
    Kannst du mit dem Code von mir aus dem Thread mit dem DGV-ScrollBars umgehen? Damit kann man sicher was machen. Ich kann dir gleich einen SplitContainer machen, ich denke das würde ich flott hinkriegen, du bist ja immer bemüht, da würde ich das Ausnahmsweise für dich anfertigen. Willst du selbst probieren oder soll ich eben?
    Kein Problem, ich habe das gerade schon probiert, funktionierte nicht. Ich vermute das es daran liegt das es keine normalen Panels sind. Bei meiner Forschung habe ich festgestellt das die Panels vom Typ Splitterpanel sind, ich denke das wir hier wieder das Problem haben:

    -Franky- schrieb:

    Meine Vermutung ist das die Textbox eben mit diesen WS_xxx Konstanten erstellt wird (ungeprüft) und daher die Message WM_CTLCOLORSCROLLBAR nicht gefeuert wird.


    Ich hab schon eine Idee für einen anderen Ansatz, die Paar Zeilen Code mach ich nach dem Mittagessen.

    PS.
    @Peter329

    Also Panels verhalten wie die SplitterPanels, so einfach wie ich dachte wird es nicht. Aber ich schaue gerade im Source vom Framework, wie das im DGV abläuft und baue das in ein Control ein. Das dauert aber, weiß nicht wie lange.

    PPS.
    @Peter329 das wird nichts. Da kann man sich ein ganz eigenen SplitContainer machen. Da muss man dann alles selbst machen. Das hat MS wirklich beschissen(um es mit aller Deutlichkeit zu sagen) gemacht. Gut das ich WinForms hinter mir lasse.

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „BitBrösel“ ()

    @'BitBrösel

    Also erst mal recht herzlichen Dank dafür, dass du dich so eingehend mit meinem Problem befasst hast. Das war wirklich sehr nett von dir.

    Dann muss ich wohl davon ausgehen, dass es für dieses Problem keine einfache Lösung gibt. Aber auch das ist hilfreich für mich. Denn so weiß ich, dass es sinnvoll ist eine Umgehung zu suchen und ich keine einfachere Lösung übersehe.

    Inzwischen hab ich das Problem folgendermaßen umgangen: Ich zeichne den Schieberegler einfach unmittelbar rechts NEBEN den Splitcontainer. Dann wird der Hintergrund des Schiebereglers eingefärbt. Das ist im Ergebnis zwar optisch nicht ganz so hübsch. Aber damit kann ich besser leben als mit einem Schieberegler, der nicht eingefärbt ist und deshalb vor allem bei größeren Intervallen nur sehr abgelesen werden kann, wodurch die Handhabung enorm erschwert wird.

    Somit hab ich eine zufriedenstellende Umgehung für mich gefunden. Recht herzlichen Dank noch einmal an alle Ratgeber, Daumen hoch und Problem gelöst.

    LG
    Peter
    Warum haust Du kein eigenes Panel in den SplitContainer, welches von der Panelklasse erbt und selber die WndProc-Methode überschreibt? Das klappt problemlos. An die nativen SplitContainerPanels wirst Du nicht rankommen, da diese als NotInheritable gekennzeichnet sind. Deren WndProc-Methode dürfte damit unzugänglich sein.
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht den Spekulatiusbackmodus wechseln.

    VaporiZed schrieb:

    Das klappt problemlos.
    Isso. ;)
    Und dass SplitterPanels was anderes als normale Panels sind, hatte ich vor Deiner Antwort im Vorpost ergänzt.
    Bilder
    • ScrollBarInOwnPanelInSplitContainer.png

      746 Byte, 497×298, 35 mal angesehen
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht den Spekulatiusbackmodus wechseln.
    Ich hab mich an die Post#1-Angaben gehalten. Da sind ja solch freistehende ScrollBars vorgegeben. Das weitere Vorgehen ist Sache von @Peter329
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht den Spekulatiusbackmodus wechseln.
    Hi,

    hier noch ein kurzer Nachtrag zu meiner Umgehung:

    Da eine Vscrollbar nicht eingefärbt wird, wenn sie Teil eines Splitcontainers ist, habe ich (wie oben ausgeführt) die Scrollbar einfach direkt neben den Splitcontainer platziert und überlagere den alten Schieberegler in der Load Prozedur.

    Dazu vergrößere ich die Breite des Splitcontainers ganz einfach ... das sind gerade mal ein paar Zeilen Code.

    So sieht das dann aus (s. Anhang). Ist doch ganz manierlich ... :)

    Natürlich muss man den Schieberegler mit dem orginalen Schieberegler synchronisieren. Dazu muss man die "First Displayed ScrollingLine" wissen ... eine DGV hat dafür eine Funktion ... eine Multiline Textbox leider nicht. Zu diesem Zweck kann man aber die folgende Funktion verwenden:

    VB.NET-Quellcode

    1. Public Const EM_GETFIRSTVISIBLELINE As UInteger = &HCE
    2. Public Const EM_LINEINDEX As UInteger = &HBB
    3. Public Const EM_LINEFROMCHAR = &HC9
    4. <DllImport("user32.dll", SetLastError:=True, CharSet:=CharSet.Auto)>
    5. Private Shared Function SendMessage(ByVal hWnd As IntPtr,
    6. ByVal Msg As UInteger,
    7. ByVal wParam As IntPtr,
    8. ByVal lParam As Integer) As IntPtr
    9. End Function
    10. ''' <summary>
    11. ''' Get the first visible line number of a multiline textbox relative to 0
    12. ''' </summary>
    13. ''' <param name="txtBox">Mulitline Textbox </param>
    14. ''' <returns>Line number of first visible line</returns>
    15. <DebuggerStepThrough>
    16. Public Function TopLineIndex(txtBox As TextBox) As IntPtr
    17. TopLineIndex = SendMessage(txtBox.Handle,
    18. EM_GETFIRSTVISIBLELINE,
    19. CType(0, IntPtr),
    20. 0&)
    21. End Function


    Damit ist es möglich, das Bewegen des Schiebereglers, das Drehen des MouseWheel und das Drücken der Pfeiltasten etc. mit der eingefärbten Scrollbar zu synchronisieren. (Man muss die TopLineNumber in den CharIndex der Zeile mit GetCharFromLine konvertieren und diesen Wert in den Value der Scrollbar eintragen). Am besten lässt man den alten Schieberegler zunächst noch sichtbar und kontrolliert, dass die Bewegungen der Scrollbars exakt synchron sind.

    Viel Spaß beim Nachbau.

    LG
    Peter
    Bilder
    • s 2022-09-29 09-48-461.jpg

      72,8 kB, 916×619, 38 mal angesehen

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