Thanks. I was not planning to call Invalidate from within Draw - that seemed like a recipe for an infinite loop.
I have attached a handler to the scroll bars and simply call invalidate in the event handler.
Then in my DrawButton function I change the state from 'Normal' to 'Hot' if the cursor position overlaps the scrollbar.
The code for the class follows.
To use this simply add the following to your initialization code:
AddHandler Me.Controls(0).MouseMove, AddressOf Scrollbar_MouseOver
AddHandler Me.Controls(1).MouseMove, AddressOf Scrollbar_MouseOver
Me.Renderer = New ScrollRenderer(Color.FromArgb(240, 247, 255), Color.FromArgb(110, 140, 170), _
Color.FromArgb(80, 90, 110), Color.FromArgb(225, 238, 255), _
Color.FromArgb(191, 219, 255), Color.FromArgb(175, 210, 255))
(Use your syntaxEditor instance instead of 'Me' if not using a derived control)
This sets the renderer to use Blue colors similar to Office, but you can use any color set.
Then add :
Protected Sub Scrollbar_MouseOver(ByVal sender As Object, ByVal e As MouseEventArgs)
sender.Invalidate()
End Sub
The class itself is:
Public Class ScrollRenderer
Inherits VisualStudio2005SyntaxEditorRenderer
Dim backColor As Color 'The background color of the scroll track
Dim borderColor As Color 'The border color around buttons
Dim iconColor As Color 'The color of the triangles or bars on buttons
Dim normalBtnColor As Color 'The default background color of buttons
Dim hotBtnColor As Color 'The background color of buttons when mouse over
Dim pressedBtnColor As Color 'The background color of buttons when pressed
Enum scrollState
Normal = 0
Hot = 1
Pressed = 3 'Really Hot + Pressed
Disabled = 16
End Enum
Public Sub New(ByVal back As Color, ByVal border As Color, ByVal icon As Color, ByVal normal As Color, ByVal hot As Color, ByVal pressed As Color)
'Store the colors for use in the override functions and set the background
backColor = back
borderColor = border
iconColor = icon
normalBtnColor = normal
hotBtnColor = hot
pressedBtnColor = pressed
SplitterBackgroundFill = New SolidColorBackgroundFill(hotBtnColor)
ScrollBarBlockBackgroundFill = New SolidColorBackgroundFill(backColor)
End Sub
Public Overrides Sub DrawScrollBarBackground(ByVal e As PaintEventArgs, ByVal bounds As Rectangle, ByVal scrollBar As ScrollBar)
'Draw the background using the appropriate appearance
SolidColorBackgroundFill.Draw(e.Graphics, bounds, backColor)
End Sub
Public Overrides Sub DrawEditorViewSplitButton(ByVal e As PaintEventArgs, ByVal bounds As Rectangle, ByVal view As EditorView, ByVal orientation As Orientation)
'Draw the small splitter bar button
DrawButton(e, bounds, scrollState.Normal, Nothing, True)
End Sub
Public Overrides Sub DrawScrollBarButton(ByVal e As PaintEventArgs, ByVal bounds As Rectangle, ByVal button As ScrollBarButton)
'Use the appropriate colors and style to draw the scroll button
DrawButton(e, bounds, button.GetDrawState(), button.ScrollBar, False)
'Draw the triangle on the button
Dim point1 As Point
Dim point2 As Point
Dim point3 As Point
If button.CommandLink.Command Is button.ScrollBar.DecreaseSmallCommand Then
If button.ScrollBar.Orientation = Orientation.Horizontal Then
point1 = New Point(bounds.Width / 3, bounds.Height / 2)
point2 = New Point(bounds.Width * 3 / 5, bounds.Height / 4)
point3 = New Point(bounds.Width * 3 / 5, bounds.Height * 3 / 4)
Else
point1 = New Point(bounds.Width / 2, bounds.Height / 3)
point2 = New Point(bounds.Width / 4, bounds.Height * 3 / 5)
point3 = New Point(bounds.Width * 3 / 4, bounds.Height * 3 / 5)
End If
ElseIf button.CommandLink.Command Is button.ScrollBar.IncreaseSmallCommand Then
If button.ScrollBar.Orientation = Orientation.Horizontal Then
Dim off As Integer = bounds.X + 1
point1 = New Point(off + bounds.Width / 3, bounds.Height / 4)
point2 = New Point(off + bounds.Width / 3, bounds.Height * 3 / 4)
point3 = New Point(off + bounds.Width * 3 / 5, bounds.Height / 2)
Else
Dim off As Integer = bounds.Y + 1
point1 = New Point(bounds.Width / 4, off + bounds.Height / 3)
point2 = New Point(bounds.Width * 3 / 4, off + bounds.Height / 3)
point3 = New Point(bounds.Width / 2, off + bounds.Height * 3 / 5)
End If
End If
Dim points As Point() = {point1, point2, point3}
e.Graphics.FillPolygon(New SolidBrush(iconColor), points)
End Sub
Public Overrides Sub DrawScrollBarThumb(ByVal e As PaintEventArgs, ByVal bounds As Rectangle, ByVal thumb As ScrollBarThumb)
'Use the appropriate colors and style to draw the thumb
If thumb.ScrollBar.Enabled Then
DrawButton(e, bounds, thumb.GetDrawState(), thumb.ScrollBar, True)
'Draw the 4 dashes in the center using an Office appearance
Dim p As Pen = New Pen(iconColor)
If thumb.ScrollBar.Orientation = Orientation.Horizontal Then
Dim mid As Integer = (bounds.Right + bounds.Left) / 2
e.Graphics.DrawLine(p, mid - 3, 5, mid - 3, bounds.Height - 6)
e.Graphics.DrawLine(p, mid - 1, 5, mid - 1, bounds.Height - 6)
e.Graphics.DrawLine(p, mid + 1, 5, mid + 1, bounds.Height - 6)
e.Graphics.DrawLine(p, mid + 3, 5, mid + 3, bounds.Height - 6)
Else
Dim mid As Integer = (bounds.Bottom + bounds.Top) / 2
e.Graphics.DrawLine(p, 5, mid - 3, bounds.Width - 6, mid - 3)
e.Graphics.DrawLine(p, 5, mid - 1, bounds.Width - 6, mid - 1)
e.Graphics.DrawLine(p, 5, mid + 1, bounds.Width - 6, mid + 1)
e.Graphics.DrawLine(p, 5, mid + 3, bounds.Width - 6, mid + 3)
End If
End If
End Sub
Private Sub DrawButton(ByVal e As PaintEventArgs, ByVal bounds As Rectangle, _
ByVal state As scrollState, ByVal sb As ScrollBar, ByVal drawBorder As Boolean)
'Paint the background of the button and maybe draw borders around it
'Change Normal state to Hot if the cursor is over the scrollbar
If sb IsNot Nothing AndAlso state = scrollState.Normal Then
Dim csr As Rectangle = New Rectangle(sb.PointToClient(Cursor.Position), New Size(1, 1))
If sb.ClipBounds.IntersectsWith(csr) Then _
state = scrollState.Hot
End If
If state = scrollState.Hot Then
e.Graphics.FillRectangle(New SolidBrush(hotBtnColor), bounds)
ControlPaint.DrawBorder(e.Graphics, bounds, borderColor, ButtonBorderStyle.Solid)
ElseIf state = scrollState.Pressed Then
e.Graphics.FillRectangle(New SolidBrush(pressedBtnColor), bounds)
ControlPaint.DrawBorder(e.Graphics, bounds, borderColor, ButtonBorderStyle.Solid)
Else
e.Graphics.FillRectangle(New SolidBrush(normalBtnColor), bounds)
If drawBorder Then _
ControlPaint.DrawBorder(e.Graphics, bounds, borderColor, ButtonBorderStyle.Solid)
End If
End Sub
End Class
Note that this draws 'flat' scroll bars, not 3D.
If you want 3D you can use the default renderer if the system colors are OK. If you want 3D in different colors you will need to change the DrawBorder function to manually draw the 8 (inner and outer) border lines manually. That may require you to pass in additional colors. (or use the Light/Dark methods of ControlPaint to modify existing colors)
Also note that the automatic highlighting of all buttons when the mouse moves over any part of the scrollbar works only with the original 2 scrollbars. If you split the view into 2 or more parts the new scroll bars in the new views will highlight only the button that the mouse is over. (You could fix that by handling the creation/deletion of views and adding/removing handlers ... but that may be overkill.)