65.9K
CodeProject 正在变化。 阅读更多。
Home

酷炫的滚动条 -类似 Windows Media Player 的滚动条

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.15/5 (24投票s)

2004年10月13日

CPOL

6分钟阅读

viewsIcon

264009

downloadIcon

2826

一个酷炫的滚动条控件。

引言

Hans Dietrich 在创建类似 Windows Media Player 的 XScrollbar 方面做得很好;然而,对于大多数 Visual Basic 程序员来说,在 Visual Basic 中单独使用它有点不方便,所以我最近用 GDI+ 编写了一个 Dietrich 的 XScrollbar 的 VB 版本——Cool Scrollbar。它目前支持

  • 滑块、轨道和控件边框上可选的颜色显示。
  • 鼠标悬停在滑块上时显示手形光标。
  • 鼠标悬停在滑块上时,滑块颜色更改为“悬停颜色”。
  • 点击轨道将滑块移动到该位置。
  • 左右箭头按钮每次移动滑块一个单位,如果一直按住,滑块会持续移动。
  • 当值更改时引发 ValueChanged 事件。
  • 滚动条的水平和垂直版本。

该控件仍处于早期阶段,可以添加许多功能。我将非常感谢任何能提高其性能的建议。

首先

GDI+ 简介

GDI+ 在两个方面与 GDI 不同

  1. GDI+ 通过提供新功能(例如,渐变画笔和 alpha 混合)来扩展 GDI 的功能。
  2. GDI+ 重新定义了编程模型,使图形编程更轻松灵活。

GDI+ 可用于显示图像、绘制自定义形状和线条、绘制字符串,甚至图像变换。在我们的项目中,我们使用它来绘制和填充形状,以及显示图像。

为了使用 GDI+,必须创建和实例化一个 Graphics 对象。例如

Dim g as Graphics = Button1.CreateGraphics

如果你想绘制线条或形状,需要一个 Pen 类。

Dim myPen as new Pen(Color.Red)

然后你可以绘制线条和形状。

g.DrawLine(myPen, 1, 1, 45, 65) 'draw line
g.DrawBezier(myPen, 15, 15, 30, 30, 45, 30, 87, 20)  'draw Bezier curve
g.DrawEllipse(myPen, New Rectangle(33, 45, 40, 50))  'draw ellipse
g.DrawPolygon(myPen, New PointF() {New PointF(1, 1), _
   New PointF (20, 10), New PointF(5, 4), New PointF(100, 2), _
   New PointF(200, 50), New PointF(39, 45)})  'draw polygon

要填充特定区域,需要一个 Brush 对象。

Dim myBrush as New SolidBrush(Color.Red)

实际上,GDI+ 中存在几种不同的画笔,如下所示:

画笔类

描述

SolidBrush (实心画笔)

纯色

HatchBrush (纹理画笔)

类似于 SolidBrush,但你可以从预设颜色中选择你想要的颜色。

TextureBrush (纹理画笔)

使用纹理(即图片)。

LinearGradientBrush

梯度

PathGradientBrush (路径渐变画笔)

复杂渐变。

我们在项目中使用的画笔是 SolidBrushLinearGradientBrushPathGradientBrush

如果你想显示图像,必须创建和实例化一个 Image 对象。例如:

Dim myBitmap as New Bitmap(System.Environment.GetFolderPath _
      (System.Environment.SpecialFolder.MyPictures))

然后你可以在特定位置将此图像加载到 Graphics 对象中。

g.DrawImage(myBitmap, 1, 1)

不要忘记在使用完 GraphicsImage 对象后释放内存。

g.Dispose()
myBitmap.Dispose()

有关详细信息,请参阅 MSDN 中的 GDI+ 参考。

术语

下图显示了 Cool Scrollbar 的组件和命名法。

绘制 Cool Scrollbar

在我开始之前,应该明确列出成员变量及其含义。在 CBar 类中,我使用了以下成员变量:

    'height of the arrow bitmap
    Private Const BitmapHeight As Byte = 12
    'width of the arrow bitmap
    Private Const BitmapWidth As Byte = 25

    'max possible value of the scrollbar
    Private m_nMaxValue As Long = 100
    'min possible value of the scrollbar
    Private m_nMinValue As Long = 0
    'absolute value of the scrollbar
    Private m_nValue As Long = 0
    'value of the scrollbar
    Private m_nRealValue As Long = 0

    'left arrow image
    Private m_imgLeftImage As Bitmap
    'right arrow image
    Private m_imgRightImage As Bitmap

    'left value of Thumbleft
    Private m_fThumbLeft As Single = 26.0F
    'flag to indicate if mouse is hover the thumb
    Private m_bMouseOnThumb As Boolean = False  
    ' flag to indicate whether left mouse button pushed
    Private m_bMouseDown As Boolean = False
    
    'which button is clicked: Left-True; Right-False
    Private m_bArrowClicked As Boolean      
       
    '''color properties
    'Border color
    Private m_cBorderColor As Color = Color.White
    'right track begin color
    Private m_cRightChannelBeginColor As Color = Color.Honeydew
    'right track end color
    Private m_cRightChannelEndColor As Color = Color.Gray
    'left track begin color
    Private m_cLeftChannelBeginColor As Color = Color.Green
    'left track end color
    Private m_cLeftChannelEndColor As Color = Color.White  
    'thumb's fill color(the left and right Half eclipse's color)
    Private m_cThumbFillColor As Color = Color.Blue
    'center color of the thumb
    Private m_cThumbRectColor As Color = Color.LightYellow  
    '''end color properties

    'layout, horizontal or vertical
    Private m_BarLayout As BarLayout = BarLayout.Horizontal

步骤 1:绘制箭头和轨道边框

绘制箭头按钮可能有点复杂,用户可能想自定义箭头图片,所以我使用位图作为箭头图片,并直接将它们绘制在控件的每一侧。最初,我将控件的高度限制为 BitmapHeight + 2 像素,箭头图片的大小为 BitmapWidth * BitmapHeight(像素)。你以后可以修改这些值。如果用户没有指定不同的图片,它将使用默认箭头,与 Dietrich 的相同。对于轨道边框,我沿着控件的整个侧面绘制一个矩形。

Dim gTrack As Graphics = Me.CreateGraphics
'draw track border
gTrack.DrawRectangle(New Pen(m_cBorderColor), _
  0, 0, Me.Width - 1, Me.Height - 1)

'draw arrow to each side of the track
gTrack.DrawImage(m_imgLeftImage, 1, 1)
gTrack.DrawImage(m_imgRightImage, Me.Width - BitmapWidth - 1, 1)

步骤 2:绘制轨道

对于这两个轨道,我使用了 LinearGradientBrush 来填充区域,这给了整个控件一种 3D 外观。用户可以通过相应的属性自定义每个轨道的颜色,我稍后会进行描述。左轨道和右轨道的画笔定义如下:

Dim rightBrush As New Drawing2D.LinearGradientBrush(ClientRectangle,_ 
        m_cRightChannelBeginColor, m_cRightChannelEndColor, _
        Drawing2D.LinearGradientMode.Vertical)
Dim leftBrush As New Drawing2D.LinearGradientBrush(ClientRectangle, _
        m_cLeftChannelBeginColor,_ m_cLeftChannelEndColor, _
        Drawing2D.LinearGradientMode.BackwardDiagonal)

绘制轨道时有两点必须牢记:

  1. 你不应该填充轨道边框;
  2. 两个轨道都排除了箭头,所以箭头区域不应该被覆盖。

因此,每个轨道的矩形区域定义如下:

Dim fTmpRightChannelWidth As Single
Dim LeftChannel As Rectangle = _
   New Rectangle(BitmapWidth + 1, 2, 0, BitmapHeight - 2), _
   RightChannel As RectangleF

LeftChannel.Width = CalValue() - BitmapWidth + BitmapHeight / 2
If LeftChannel.Width <= 0 Then
  LeftChannel.Width = BitmapHeight / 2
  fTmpRightChannelWidth = Me.Width - BitmapWidth * 2 - 2
  RightChannel = New RectangleF(BitmapWidth + 1, 1, _
    fTmpRightChannelWidth, BitmapHeight)
'The function CalValue() returns the current
' thumb's left edge's x-coordinate.
Private Function CalValue() As Single
    Return BitmapWidth + 1 + (Me.Width - BitmapWidth * 3 - 2) _
       * m_nValue / (m_nMaxValue - m_nMinValue)
End Function

接下来,使用上面定义的画笔填充相应的区域。

gTrack.FillRectangle(rightBrush, RightChannel)
gTrack.FillRectangle(leftBrush, LeftChannel)
gTrack.DrawRectangle(New Pen(Color.Gray), LeftChannel.X, LeftChannel.Y, _
  LeftChannel.Width, LeftChannel.Height - 1)

步骤 3:绘制滑块

直到最近,我才找到一种好看的滑块样式,所以目前滑块的样式看起来可能有点难看;然而,绘制过程几乎相同。

滑块的大小与箭头的大小相同。在绘制滑块之前,你必须先计算滑块的位置。这是通过 CalValue() 函数完成的,如前所述。滑块区域由两端的扇形区域和中间的矩形区域组成。我使用 GraphicsPath 类来创建这样的区域,并使用 PathGradientBrush 来填充该区域。

        Dim gThumb As Graphics = Me.CreateGraphics
        'pen to draw the edge of the area
        Dim linePen As New Pen(Color.Gray, 1)
        Dim fX(3) As Single

        'initialize edge values--------------------------------------
        fX(0) = CalValue()
        If (m_BarLayout = BarLayout.Horizontal) Then
            fX(1) = fX(0) + BitmapHeight / 2
            fX(2) = fX(1) + (BitmapWidth - BitmapHeight) / 2 '7
        Else
            fX(1) = fX(0) + BitmapWidth / 2
            fX(2) = fX(1) + (BitmapHeight - BitmapWidth) / 2 '7
        End If
        '''----------------------------------------------------------

        'define path and brushes
        Dim rectPath As New Drawing2D.GraphicsPath
        'path that will constitute the thumb area
        If (m_BarLayout = BarLayout.Horizontal) Then
            Dim rect2Fill As New RectangleF(fX(1), 1.0F, _
                BitmapWidth - BitmapHeight, BitmapHeight)
                'center rectangle area of the thumb
            Dim rect2Fill As New RectangleF(fX(1), 1.0F, fX(2)_
               - fX(0), BitmapHeight)
               'center rectangle area of the thumb
            rectPath.AddArc(fX(0), 0.3F, BitmapHeight, _
                        BitmapHeight, 90, 180) 'left pie
            rectPath.AddRectangle(rect2Fill) 'center
            rectPath.AddArc(fX(2), 0.3F, BitmapHeight, _
                        BitmapHeight, 90, -180) 'right pie
        Else
            Dim rect2Fill As New RectangleF(1.0F, fX(2), _
                    BitmapWidth, 2 * fX(1) - fX(0) - fX(2))
            rectPath.AddArc(0.3F, 2 * fX(1) - fX(0), _
                    BitmapWidth, BitmapWidth, 0, 180) 'upper pie
            rectPath.AddRectangle(rect2Fill) 'center
            rectPath.AddArc(0.3F, fX(2) - fX(1) + fX(0), _
                    BitmapWidth, BitmapWidth, -180, 180) 'down pie
        End If
        Dim rectBrush As New Drawing2D.PathGradientBrush(rectPath)
        rectBrush.CenterColor = m_cThumbRectColor
        Dim rectColors As Color() = {m_cThumbFillColor, _
            m_cThumbFillColor, m_cThumbFillColor, m_cThumbFillColor}
        rectBrush.SurroundColors = rectColors

        'draw the thumb
        If (m_BarLayout = BarLayout.Horizontal) Then
            gThumb.DrawArc(linePen, fX(0), 1.0F, _
                   BitmapHeight, BitmapHeight - 1, 90, 180)
            gThumb.DrawArc(linePen, fX(2), 1.0F, _
                   BitmapHeight, BitmapHeight - 1, -90, 180)
        Else
            gThumb.DrawArc(linePen, 1.0F, 2 * fX(1) - fX(0), _
                   BitmapWidth, BitmapWidth - 1, 0, 180)
            gThumb.DrawArc(linePen, 1.0F, fX(2) - fX(1) + fX(0), _
                   BitmapWidth, BitmapWidth - 1, 0, -180)
        End If
        gThumb.FillPath(rectBrush, rectPath)

步骤 4:将它们组合在一起

每次滚动条的值发生变化时,只需执行前三个步骤中描述的任务,滚动条就会显示为文章开头所描述的那样。

将我们的滚动条变成真正的酷炫滚动条

添加重写

到目前为止,我们的滚动条已经有了“面孔”,但它不仅仅是一个图片。为了成为一个真正的滚动条,首先它应该能够滚动。借助从 System.Windows.Forms.UserControl 类继承的大量重写子程序和函数,可以轻松实现这一点。

如命名法部分所述,滚动条有三个区域:轨道、滑块和箭头按钮。因此,当鼠标在这些区域单击或移动时,应该有不同的事件。有关更多详细信息,请参考我的代码,这一部分写得尽可能清楚,以便你能够很好地理解。

添加属性

至于属性,我目前启用了以下内容:

行为

  • Value:滚动条的当前值(Long
  • MinValue:滚动条的最小值(Long
  • MaxValue:滚动条的最大值(Long

外观

  • LeftArrow:左箭头图像(Bitmap
  • RightArrow:右箭头图像(Bitmap
  • LeftChannelBeginColor:左轨道画笔的起始颜色(Color
  • LeftChannelEndColor:左轨道画笔的结束颜色(Color
  • RightChannelBeginColor:右轨道画笔的起始颜色(Color
  • RightChannelEndColor:右轨道画笔的结束颜色(Color
  • ThumbFillColor:滑块区域的中心颜色(Color
  • ThumbRectColor:滑块区域的周围颜色(Color
  • TrackBorderColor:轨道边框的颜色(Color
  • ScrollbarLayout:滚动条的布局,垂直或水平。

启用这些属性的方法与普通方法相同,只需记住在每次属性值更改时重绘轨道。

在 Windows 应用程序属性窗口中,它们看起来是这样的。

你可以根据需要修改它们。

添加事件

目前 Cool Scrollbar 只需一个事件即可正常工作:ValueChanged。每次发生 MouseUp 事件时,只需引发此事件。

未来增强功能

  • 更时尚的滑块
  • 灵活的尺寸属性
  • 可选的工具提示文本,指示滚动条的值。
  • 更多...

致谢

感谢Hans Dietrich 提供创建 XScrollBar 的想法以及对绘制过程的清晰解释。

历史

版本 0.1 - 2004 年 10 月 12 日

  • 首次公开发布。

版本 0.2 - 2005 年 4 月 25 日

  • 添加了滚动条的垂直版本。
  • 更新了滑块的绘制方法。
© . All rights reserved.