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

增强的滚动条

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.98/5 (50投票s)

2013年7月31日

CPOL

17分钟阅读

viewsIcon

108087

downloadIcon

6794

具有增强属性、图形书签和值工具提示的 WinForms 滚动条。

引言

在我最近的一个项目中,我需要滚动大量的项目。确切地说,我想滚动整个物理驱动器的内容。可以想见,开箱即用的 WinForms ScrollBar 控件,其 MaximumMinimumValue 属性都定义为整数 (Int32),对于这个目的来说并不十分有用。

实际上,限制甚至更严格。在设计模式下:将 Maximum 设置为大于 1,000,000,000 的任何值,并将 Minimum 设置为小于 -1,000,000,000 的任何值,都会导致设计时异常(消息为“属性值无效”)。另一方面,您可以从代码中将 MinimumMaximum 设置为任何有效的整数 (Int32) 值。不会抛出异常。但是,当表达式 Maximum - Minimum 的计算结果大于 int.MaxValue -1, 时,您可能会观察到奇怪的行为。Maximum 的值会突然变为与您想要的不同。最好不要尝试使用 Microsoft 的 ScrollBar 和大数字。为此,您有本文介绍的 ScrollBarEnhanced

如我上面所说

  • 我希望 ScrollBar 至少能接受 long 类型的值作为 MaximumMinimumValue 属性。
  • 我还希望能够为某些 ScrollBar 值添加书签,并在 ScrollBar 本身上将这些书签值显示为彩色的小点、方块或条形。
  • 最后,我希望能够在鼠标移动到书签或未标记的轨道区域上方时显示动态工具提示。

如果您还记得 Visual Studio 中带有彩色图形装饰的垂直 ScrollBar,那么这就是我想要实现的目标。

背景

在 CodeProject 上快速搜索后,我发现了很多关于 Scrollbar 的文章,但没有一篇完全满足我的需求。

在解决问题的第一个尝试中,我构建了一个继承自 VScrollBar 的自定义控件(当时我不在乎水平滚动条,我只需要垂直滚动条)。我能够覆盖 MaximumMinimumValue 属性以接受 long 类型的值。但是,首先,这很ugly(将包装器中的 long 值转换为底层 VScrollBarint 值,反之亦然,并且我仍然无法显示书签的图形装饰,动态工具提示也不容易实现)。

启发我编写自己的 ScrollBar(我决定称之为 ScrollBarEnhanced)是 CodeProject 上 Greg Ellis 的文章 的文章《如何在 C# 中为面板创建滚动条皮肤》。这篇文章向我展示了从头开始构建 ScrollBar 并不难,而且编写自己的 ScrollBar 可以为我提供更多的定制和更多功能,如果我以后需要的话。

在我决定从头开始编写 ScrollBarEnhanced 后,我很快就将我的要求升级为使用 decimal 而不是 long 来表示 MaximumMinimumSmallChageLargeChangeValue 属性。

Decimal 具有 28-29 位有效数字的精度,其近似值范围约为 ±1.0 × 10-28 至 ±7.9 × 1028。这比 long 的值范围 –9,223,372,036,854,775,808 到 9,223,372,036,854,775,807(“只有”19 位有效数字)要好得多。decimal 精度更高的原因是,decimal 是 128 位数据类型,而 long 仅为 64 位。使用 decimal 的最终论据是 WPF 版本的 ScrollBar 使用 decimal,所以为什么不在 WinForms 版本中也具有类似的行为呢。

要编写我自己的 ScrollBarEnhanced 实现,我需要编程以下内容:

  • 绘制滚动条的元素(箭头、滑块、轨道条和抓手)。通过使用 ScrollBar 控件的 ScrollBarRenderer static 方法,这变得非常容易。所有绘制都应在重写的 OnPaint 处理程序中进行。
  • 同样在 OnPaint 处理程序中绘制书签标记的图形表示。也不难,因为我只需要绘制矩形(用于方块和条形)和椭圆(用于点)。
  • 检测鼠标移动和单击(在 OnMouseMoveOnMouseDownOnMouseUpOnMouseLeaveOnMouseClick 处理程序中),并定义一些增强的事件来将信息传递回宿主。
  • MaximumMinimumSmallChageLargeChageValue 属性定义为 decimal 类型。

概述

作为开始,我在 Visual Studio 中创建了一个继承自 UserControlScrollBarEnhanced 自定义控件。

public class ScrollbarEnhanced : UserControl 

该控件封装了 VerticalHorizontal ScrollBar 的功能,并添加了几项增强功能以满足我的需求。

所有绘图都在 OnPaint 中进行。下面我展示了一个简化的版本,它绘制基本的 ScrollBar。实际代码有更多行,以处理绘制热点或按下状态的滚动条元素的不同方式。我在这里删除了它们,以展示 ScrollBarEnhanced 的基本绘图有多么简单:只需几次调用 ScrollBarRenderer 绘图方法即可——仅此而已。

protected override void OnPaint(PaintEventArgs e) 
{ 
   int ArrowHeight = SystemInformation.VerticalScrollBarArrowHeight;
 
   //Draw top arrow
   ScrollBarRenderer.DrawArrowButton(e.Graphics,rec, ScrollBarArrowButtonState.UpNormal);
           
   //Draw top track
   int ThumbPos = Value2ThumbTopPosition(Value);
   rec = new Rectangle(0, ArrowHeight + 1, ClientSize.Width, ThumbPos - ArrowHeight);
   ScrollBarRenderer.DrawUpperVerticalTrack(e.Graphics, rec, ScrollBarState.Normal);
 
   //draw thumb
   int nThumbHeight =ThumbHeight;
   rec = new Rectangle(0, ThumbPos, ClientSize.Width, nThumbHeight);
   ScrollBarRenderer.DrawVerticalThumb(e.Graphics, rec, ScrollBarState.Normal);
 
   //draw thumb grip
   ScrollBarRenderer.DrawVerticalThumbGrip(e.Graphics, rec, ScrollBarState.Normal);
 
   //Draw bottom track
   rec = new Rectangle(0, ThumbPos + nThumbHeight, 
             ClientSize.Width, TrackHeight - (ThumbPos + nThumbHeight));
   ScrollBarRenderer.DrawLowerVerticalTrack(e.Graphics, rec, ScrollBarState.Normal);
 
   //Draw bottom arrow 
   rec = new Rectangle(0, ClientSize.Height - ArrowHeight, ClientSize.Width, ArrowHeight);  
   ScrollBarRenderer.DrawArrowButton(e.Graphics, rec, ScrollBarArrowButtonState.DownNormal);
 
   //Draw  bookmarks (on top of everything)
   if (Bookmarks != null)
   {
      foreach (ScrollBarBookmark bk in Bookmarks)
      {
         DrawBookmark(e.Graphics, bk);
      }
  }
}

OnPaint 调用 DrawBookmark 方法用于在 ScrollBarEnhanced 控件上显示书签标记。它们有三种类型的书签:ImageScrollBarBookmarkBasicShapeScrollBarBookamrkValueRangeScrollBarBookmark。这三种类型都继承自 ScrollBarBoookmarkAbstract 类。

  • 顾名思义,ImageScrollBarBookmark 是一个绘制为小图像的书签。图像的中心位于书签的 Value 属性。
  • BasicShapeScrollBarBookmark 是绘制在 scrollbar 主体上的彩色矩形或方块。形状的中心位于书签的 Value 属性。
  • ValueRangeScrollBarBookmarkBasicShapeScrollBarBookmark 的一个变体。但是,虽然前两种书签是“点书签”:它们与滚动条的确切值相关联,但 ValueRangeScrollBarBookmark 是与值范围相关联的书签。它还有一个额外的属性 EndValue,与 Value 一起定义范围。ValueRangeScrollBarBookmark 可用于标记滚动条关联的元素范围。例如,如果您想使用 ScrollBarEnhanced 来滚动文本编辑器中的文本行,您可以使用范围滚动条来标记选定的文本行。演示程序的截图有一个 ValuRangeScrollBarBookmars 的示例:一个绘制为右对齐的橙色线,另一个绘制为绿色(亮绿色)、左对齐的线。
private void DrawBookmark(Graphics Graphics, ScrollBarBookmark Bookmark)
{
    BasicShapeScrollBarBookmark shapeBookmark = null;
    ImageScrollBarBookmark imageBookmark=null;
    if (Bookmark is BasicShapeScrollBarBookmark)
        shapeBookmark = (BasicShapeScrollBarBookmark)Bookmark;
    else
        imageBookmark = (ImageScrollBarBookmark)Bookmark;

    //Make sure that brush needed for drawing is ready to use
    if (shapeBookmark != null)
    {
        if (shapeBookmark.Brush == null)  //this bookmark is used to the first time
        {
            if (!m_BrushesCache.ContainsKey
            (shapeBookmark.Color))  //this color is used for the first time
                m_BrushesCache.Add(shapeBookmark.Color, new SolidBrush(shapeBookmark.Color));

            shapeBookmark.Brush = m_BrushesCache[shapeBookmark.Color];
        }
        //Make sure that pen needed for drawing is ready to use
        if (shapeBookmark.Pen == null)  //this bookmark is used to the first time
        {
            if (!m_PensCache.ContainsKey
            (shapeBookmark.Color))  //this color is used for the first time
            {
                m_PensCache.Add(shapeBookmark.Color, new Pen(shapeBookmark.Color));
            }
            shapeBookmark.Pen = m_PensCache[shapeBookmark.Color];
        }

        if (shapeBookmark.Stretch)
        {
            Bookmark.X = 0;
            shapeBookmark.Width = this.ClientSize.Width;
        }
        else
        {
            CalculateBookmarkXPosition(Bookmark);
        }
    }
    else  //Calculate X for ImageBookmark
    {
        CalculateBookmarkXPosition(Bookmark);
    }

    //Calculate top Y position of bookmark 
    Bookmark.Y = (int)(TrackHeight * Bookmark.Value / Maximum + 
      SystemInformation.VerticalScrollBarArrowHeight - Bookmark.Height / 2);

    if (imageBookmark != null)
    {
        Graphics.DrawImage(imageBookmark.Image, new Point(Bookmark.X, Bookmark.Y)); 
    }
    else  if (shapeBookmark.FillBookmarkShape)
    {
        if (shapeBookmark.Shape == ScrollbarBookmarkShape.Oval)
            Graphics.FillEllipse(shapeBookmark.Brush, new Rectangle(Bookmark.X, 
               Bookmark.Y, shapeBookmark.Width, shapeBookmark.Height));
        else
            Graphics.FillRectangle(shapeBookmark.Brush, new Rectangle(Bookmark.X, 
               Bookmark.Y, shapeBookmark.Width, shapeBookmark.Height));
    }
    else  
    {
        if (shapeBookmark.Shape == ScrollbarBookmarkShape.Oval)
            Graphics.DrawEllipse(shapeBookmark.Pen, new Rectangle(Bookmark.X, 
              Bookmark.X, shapeBookmark.Width, shapeBookmark.Height));
        else
            Graphics.DrawRectangle(shapeBookmark.Pen, new Rectangle(Bookmark.X, 
              Bookmark.X, shapeBookmark.Width, shapeBookmark.Height));
    }
}
private void CalculateBookmarkXPosition(ScrollBarBookmark Bookmark)
{
    switch (Bookmark.Alignment)
    {
        case ScrollBarBookmarkAlignment.Left:
            Bookmark.X = 0;
            break;
        case ScrollBarBookmarkAlignment.Right:
            Bookmark.X = this.ClientSize.Width - Bookmark.Width;
            break;
        case ScrollBarBookmarkAlignment.Center:
            Bookmark.X = (this.ClientSize.Width - Bookmark.Width) / 2;
            break;
    }
}

上面提到的 ScrollBarBookmark 类需要更多关注。您只有在要显示滚动条上的书签时才会使用它。ScrollBarBookmark 是一个 abstract 类,它包含以下定义的书签基本属性:

public abstract class ScrollBarBookmark
{
    [DefaultValue(0)]
    public decimal Value { set; get; }

    [DefaultValue(ScrollBarBookmarkAlignment.Left)]
    public ScrollBarBookmarkAlignment Alignment { set; get; }

    //e.g. Bookmark object from host can contain any sort of info
    //can be handy when bookmark is clicked; moved over etc.
    [DefaultValue(null)]
    public object Tag { set; get; }

    public virtual int Height { set; get; }
    public abstract int Width { set; get; }

    //Every time bookmark is repainted the following two values are 
    //Populated with X, Y of bookmark center
    protected internal int X { set; get; }
    protected internal int Y { set; get; }
}  

“实际”书签存储在继承自 ScrollBarBookmark 的三个类之一的实例中。

  • ImageScrollBarBookmark - 此类允许使用图像作为其图形表示来定义书签。
  • BasicShapeScrollBarBookmark - 此类允许将书签定义为基本形状(矩形或椭圆)。
  • ValueRangeScrollBarBookmark - 技术上,此类继承自 BasicShapeScrollBarBookmark,并且它只有一个额外的属性:EndRange 它允许绘制书签,而不是在提供的值处绘制一个点,而是绘制一个范围。通常您会为此目的使用矩形形状。

下面定义了 ImageScrollBarBookmark

public class ImageScrollBarBookmark : ScrollBarBookmark
{
    public ImageScrollBarBookmark()
    {
        this.Image = null;
        this.Value = 0;
        this.Alignment = ScrollBarBookmarkAlignment.Left;
        this.Tag = null;
    }
    public ImageScrollBarBookmark(decimal Value, Image Image, 
            ScrollBarBookmarkAlignment Alignment, object Tag)
    {
        this.Image = Image;
        this.Value = Value;
        this.Alignment = Alignment;
        this.Tag = Tag;
    }
    public Image Image { set; get; }
    public override int Width 
    {
        get { return Image.Width; }
        set {  ;}
    }
    public override  int Height
    {
         get { return Image.Height; }
        set {;}
    }
} 

下面是 BasicShapeScrollBarBookmark

public BasicShapeScrollBarBookmark()
{
    Value = 0;
    Alignment = ScrollBarBookmarkAlignment.Left;
    Height = 5;
    Width = 5;
    Shape = ScrollbarBookmarkShape.Rectangle;
    Color = Color.Orange;
    FillBookmarkShape = true;
    Stretch = false;
    Tag = null;
}

public BasicShapeScrollBarBookmark(decimal ScrollValue, 
       ScrollBarBookmarkAlignment Alignment, int Height, 
       int Width, bool Stretch, object Tag)
{
    this.Value = ScrollValue;
    this.Alignment = Alignment;
    this.Height = Height;
    this.Width = Width;
    this.Stretch = Stretch;  //Stretch to smallest scrollbar dimension
    this.Tag = Tag;
    this.Shape = ScrollbarBookmarkShape.Rectangle;
    this.Color = Color.Empty;
    this.FillBookmarkShape = true;
}

public BasicShapeScrollBarBookmark(decimal ScrollValue, 
       ScrollBarBookmarkAlignment Position, int Height, int Width, 
       ScrollbarBookmarkShape Shape, Color Color, bool FillBookmarkShape, 
       bool StretchToScrollBarWidth, object Tag)
{
    this.Value = ScrollValue;
    this.Alignment = Position;
    this.Height = Height;
    this.Width = Width;
    this.Shape = Shape;
    this.Color = Color;
    this.FillBookmarkShape = FillBookmarkShape;
    this.Stretch = StretchToScrollBarWidth;
    this.Tag = Tag;
}

int m_Width = 0;
[DefaultValue(4)]
public override int Width
{ 
     set { m_Width = value; } 
     get { return m_Width; } 
}

int m_Height = 0;
[DefaultValue(4)]
public override int Height 
{ 
    set { m_Height = value; }
    get { return m_Height; } 
}

[DefaultValue(false)]
public bool Stretch { set; get; }

[DefaultValue(ScrollbarBookmarkShape.Rectangle)]
public ScrollbarBookmarkShape Shape { set; get; }

[DefaultValue(true)]
public bool FillBookmarkShape { set; get; }

private Color m_Color = Color.Empty;
[DefaultValue(typeof(Color), "Orange")]
public Color Color 
{
    set
    {
        if (m_Color != value)
        {
            m_Color = value;
            Pen = null;
            Brush = null;
        }
    }
    get { return m_Color; }
}
 
[NonSerialized]
private Pen m_Pen;
protected internal Pen Pen 
{
    set { m_Pen = value; }
    get { return m_Pen; }
}

[NonSerialized]
private Brush m_Brush;
protected internal Brush Brush
{
    set { m_Brush = value; }
    get { return m_Brush;  }
}

ValueRangeScrollBarBookmarkBasicShapeScrollBarbookmark 中添加了一个额外的属性:EndValue

public class ValueRangeScrollBarBookmark : BasicShapeScrollBarBookmark
{

    public ValueRangeScrollBarBookmark()
    {
        Name = "";
        Value = 0;
        EndValue = 0;
        Alignment = ScrollBarBookmarkAlignment.Left;
        Height = 5;
        Width = 5;
        Shape = ScrollbarBookmarkShape.Rectangle;
        Color = Color.Orange;
        FillBookmarkShape = true;
        Stretch = false;
        Tag = null;
    }



    public ValueRangeScrollBarBookmark(string Name, decimal StartValue, 
        decimal EndValue, ScrollBarBookmarkAlignment Alignment, int Depth, 
        Color Color, bool FillBookmarkShape, bool StretchToScrollBarWidth, object Tag):
        base(Name, StartValue, Alignment, 0, Depth, ScrollbarBookmarkShape.Rectangle, 
        Color, FillBookmarkShape, StretchToScrollBarWidth, Tag)
    {
        this.EndValue = EndValue;
    }

    public decimal EndValue { set; get; }

    int m_Width = 0;
    [DefaultValue(4)]
    [Description("ValueRangeScrollBarBookmark Height (for vertical) 
      or Width (for horizontal) are calculated every time scrollbar is repainted. 
      Value will change every time Minimum, Maximum or scrollbar size change.")]
     public override int Width
    {
        set { m_Width = value; }
        get { return m_Width; }
    }

    int m_Height = 0;
    [DefaultValue(4)]
    [Description("ValueRangeScrollBarBookmark Height (for vertical) 
      or Width (for horizontal) are calculated every time scrollbar is repainted.
      Value will change every time Minimum, Maximum or scrollbar size chage.")]
    public override int Height
    {
        set { m_Height = value; }
        get { return m_Height; }
    }
}

ValueRangeScrollBookmarkOnPaint 方法中绘制时,它的长度(对于垂直滚动条是高度,对于水平滚动条是宽度)将被计算。

int bookmarkLength =(Bookmark.EndValue - Bookmark.Value) / (Maximum - Minimum)) * TrackLength);

最后是上面代码中使用的 enums 的定义。

public enum ScrollbarBookmarkShape
{
    Rectangle,
    Oval,
}
public enum ScrollBarBookmarkAlignment
{
    Left,
    Right,
    Center
}

通常,您会从代码中分配书签。例如,您的程序用户点击了带有附加滚动条的页面上的某个位置,并希望为该位置添加书签以便快速参考。

这是 BasicShapeScrollBarBookmark 属性的简要说明:

  • Value - ScrollBarEnhanced 的 Value 属性,对应于书签图形表示的中心。
  • Height - 书签图形元素的高度;默认值为 5 像素。
  • Width - 书签图形元素的宽度;默认值为 5 像素。
  • Stretch - 如果此属性设置为 true,则会忽略 Width 和 Position 属性,并将书签显示为矩形或椭圆(取决于 Shape 属性值),其宽度等于滚动条控件实例的宽度。
  • Shape - 书签形状枚举器:可以是 Rectangle 或 Oval。
  • FillBookmarkShape - 如果设置为 true,则书签主体将填充与书签边框相同的颜色,否则只显示书签的边框线。由于书签标记通常很小,您很可能希望将此属性设置为 true 以使标记更明显。
  • Color - 书签颜色;用于绘制书签边框和主体的颜色。
  • Alignment - 书签位置枚举器。可以是 Left、Center 或 Right。
  • Tag - 任何内容都可以在这里。如果您想为书签对象分配比预定义属性列表更多的信息,这可能会很有用。ScrollBarEnhanced 不会使用它,但由于某些增强事件会将原始书签对象返回给宿主(例如,鼠标正在悬停的书签列表),在某些情况下,宿主可能会发现 Tag 值很有用。

ImageScrollBarBookmark 具有略微不同的属性集。

  • Value - ScrollBarEnhancedValue 属性,对应于书签图形表示的中心。
  • Image - 将作为书签显示的图形元素。
  • Height - 书签图形元素的高度(图像的高度)。这是一个只读属性。
  • Width - 书签图形元素的宽度(图像的宽度)。这是一个只读属性。
  • Alignment - 书签位置枚举器。可以是 Left、Center 或 Right。
  • Tag - 任何内容都可以在这里。如果您想为书签对象分配比预定义属性列表更多的信息,这可能会很有用。ScrollBarEnhanced 不会使用它,但由于某些增强事件会将原始书签对象返回给宿主(例如,鼠标正在悬停的书签列表),在某些情况下,宿主可能会发现 Tag 值很有用。

ValueRangeScrollBarBookmark 具有与 BasicShapeScrollBarBookmark 完全相同的属性,外加一个额外的属性。

  • EndValue - 与 Value 一起定义值的范围,并有效地定义绘制书签的形状的大小。

除了 OnPaint 之外,还有几个方法在代码中起着关键作用。下面简要介绍它们。如果您想要更多细节,请查看本文附加的 ScrollBarEnhanced 源代码。

  • OnMouseDown - 保存当前鼠标位置。触发适当的 OnScroll 事件。如果鼠标按下在向上或向下箭头处,则将 Value 减少或增加 SmallChange 量。如果鼠标按下在上方或下方轨道区域,则将 Value 减少或增加 LargeChage
  • OnMouseMove - 如果鼠标已按下且位于滑块上方,则执行滑块拖动。否则,以附加列表值作为参数触发 MouseMove 事件。
  • OnMouseUp - 以附加列表值作为参数触发 Scroll 事件。
  • OnMouseClick - 以附加列表值作为参数触发 MouseClick 事件。
  • OnMouseLeave - 清理其他鼠标操作。

Using the Code

您可以使用 ScrollBarEnhanced 控件,就像使用常规的 WinForms VScrollBar 或 HScrollBar 一样。ScrollBarEnhanced 具有 Orientation 属性,该属性使滚动条相应地表现为垂直或水平滚动条。您只需将其拖放到所需的窗体上,设置必要的属性,连接您想要拦截的事件,然后就可以开始了。

您可以使用的增强属性是:

  • Bookmarks - ScrolBarBookmark 对象的集合。每个元素定义书签图形标记的位置和显示方式。您可以定义值、位置、大小、颜色和形状。
  • BookmarksOnTop - 此属性定义书签是否作为最顶部的项绘制(当设置为 true 时)。这意味着滚动条滑块可能会被书签部分覆盖。如果此属性设置为 false,则最顶部的项是滚动条滑块。
  • EndValue - 与 Value 属性一起定义滚动条定义的范围。EndValue 仅在 ValueRangeScrollBarBookmark 中定义。
  • ContextMenuStrip - 此属性继承自基类控件,但与标准 VScrollBar 和 HSrollBar 的上下文菜单不同,它可以完全自定义。您可以拦截 Opening 事件并在显示上下文菜单之前修改它,或者简单地用您自己的上下文菜单替换它。
  • InitialDelay - 标准 VScrollBar 有一个我想要复用的很酷的功能。当您按住左鼠标按钮时,它会重复单击操作,而无需您实际单击。这种行为在按住鼠标一段时间后开始,该时间由 InitialDelay 属性定义(以毫秒为单位)。InitialDelay 的值通常大于 RepeatInterval。默认值为 400 毫秒。
  • Orientation - 定义 ScrollBarEnhanced 是作为垂直滚动条还是水平滚动条行为的枚举器。
  • QuickToolbarNavigtion - 如果设置为 true,则通过单击书签图形表示即可快速导航到书签值。
  • RepeatInterval - 在按住鼠标超过 InitialDelay 毫秒后触发自动重复行为后,所有后续的自动生成单击都以 RepeatInerval 定义的间隔发生。默认值为 62 毫秒。
  • ShowTooltipOnMouseMove - 如果此属性设置为 true,则每次用户将鼠标移到滚动条轨道上方时,都会显示工具提示。默认情况下,工具提示文本显示鼠标正在经过的值。可以通过处理 TooltipNeeded 事件并更改传递给事件处理程序的事件参数对象的 ToolTip 属性来覆盖工具提示文本。
  • ValueMinimumMaximumSmallChageLargeChage - 它们的含义与标准 VScrollBarHScrollBar 控件相同,但现在它们的类型都为 decimal。

增强事件

ScrollBarEnhanced 中定义了三个新事件,这些事件在标准的 VScrollBarHScrollBar 中不存在。

  • OrienationChanged - 每次 ScrollBarEnhanced 的 Orientation 属性更改时触发。
  • OrientationChanging - 每次 Orientation 属性的值即将更改时触发。此事件可取消。
  • ToolTipNeeded - 我希望能够捕获滚动条需要显示工具提示的所有情况,并能够使用默认工具提示值或动态更改它。

以下是 ScrollBarEnhanced 中定义的所有事件的简要说明:

  • MouseClick - 每次在滚动条轨道区域单击鼠标时发生此事件。此事件与 VScrollBarHScrollBarMouseClick 相同。
  • MouseMove - 每次鼠标在 ScrollBarEnhanced 控件上移动时发生此事件。有关传递给事件处理程序的参数的详细信息,请参阅 EnhancedMouseEventArgs
  • OrientationChanging - 每次 ScrollBarEnhanced 的方向即将更改时发生此事件。用户有机会取消更改 Orientation 属性值。
  • OrientaionChange - 在方向属性更改后每次发生此通知。
  • TooltipNeeded - 每次鼠标移动到轨道条区域或书签标记上方时(为了触发此事件,ShowToooltipOnMouseMove 属性必须设置为 true)会触发此事件。通过处理此事件,您可以修改默认的工具提示值。有关传递给事件处理程序的参数的详细信息,请参阅 ToolTipNeededEventArgs
  • Scroll - 模仿标准 VScrollBarHScrollBarScroll 事件。有关传递给事件处理程序的参数的详细信息,请参阅 EnhancedScrollEventArgs
  • ValueChanged - 每次滚动条值更改时发生。此事件与标准的 VScrollBar 或 HScrollBar ValueChanged 事件相同。

增强事件参数

EnhancedMouseEventArgs - 此对象用于将信息传递给 MouseMoveMouseClick 事件处理程序。请查看 Bookmarks 属性。它是一个集合。原因是 ScrollBarEnhanced 的一个单一垂直像素可以映射到多个书签。首先,没有什么能阻止您为同一个 ScrollBarEnhanced 值定义多个书签。其次,即使您被保护免于为同一值分配多个书签,您仍然可能会发现多个书签显示在相同的 ScrollBar 像素上。列表中的书签的显示顺序与其在 Bookmarks 属性列表中的顺序相同。最后一个元素最后绘制,所以在显示时它出现在顶部。让我们考虑以下(有点极端的)示例。您想滚动整个 1Tb(太字节)大小的物理驱动器内容(约 1012 字节)。假设您将在显示的一行中显示 100 字节。这意味着您需要将 ScrollbarEnhanced 的 Maximum 设置为 1012/100=1010)- 这就是您想要滚动的行数。最后,假设您的 ScrollBarEnhanced 控件显示在轨道条的 1000 个垂直像素上。这意味着您必须将 <0, 1010> 的 ScrollBarEnhanced 地址空间映射到 <0, 1000> 的显示像素。因此,1 个像素将被映射到 1010/1000= 107(1000 万)个值。所以即使您将一个书签设置在 0 值,另一个设置在 5,000,000,两个书签都会显示在屏幕的相同位置。

EnhancedScrollEventArgs - 此定义与 VScrollBar 中使用的 ScrollEventArs 几乎相同。唯一区别是 OldValueNewValue 是 decimal 而不是 int。

public class EnhancedMouseEventArgs:MouseEventArgs
{
    public EnhancedMouseEventArgs(decimal Value, MouseEventArgs MouseArgs,
           List<ScrollBarBookmark> Bookmarks,
           EnhancedScrollBarMouseLocation ScrollBarSection )
           :base(MouseArgs.Button, MouseArgs.Clicks, MouseArgs.X, MouseArgs.Y,
           MouseArgs.Delta)
    {
        this.Value = Value;
        this.Bookmarks = Bookmarks;
        this.ScrollBarSection = ScrollBarSection;
    }
    public decimal Value {private set; get;}
    public List<ScrollBarBookmark> Bookmarks {private set; get;}
    public EnhancedScrollBarMouseLocation ScrollBarSection { private set; get; }
}

ToolTipNeededEventArgs - 用于 ToolTipNeeded 事件。它传递关于需要工具提示的 ScrollBarEnhanced 点的扩展信息。ToolTipNeededEventArgs 定义了以下属性:

  • Value - 需要工具提示文本的 ScrollBarEnhanced Value 属性。
  • ToolTip - ScrollBarEnhanced 希望显示的默认工具提示值。此值可以更改。
  • Bookmarks - 鼠标悬停在其上方的书签列表。列表中的最后一个书签代表屏幕上最顶部的书签,通过拥有此列表,您可以根据书签上下文显示工具提示。
public class TooltipNeededEventArgs : EventArgs
{
    public TooltipNeededEventArgs(decimal Value, string ToolTip,
    List<ScrollBarBookmark> Bookmarks)
    {
        this.Value=Value;
        this.ToolTip=ToolTip;
        this.Bookmarks = Bookmarks;
    }
    public decimal Value { private set; get; }
    public string ToolTip {set; get;}
    public List<ScrollBarBookmark> Bookmarks { set; get; }
}
public decimal OldValue {private set; get;}
public decimal NewValue {private set; get;}
    public ScrollOrientation ScrollOrientation {private set; get;}
    public ScrollEventType Type {private set; get;}
}

注释

  1. 我决定“修复”默认的 VScrollBar 和 HScrollBar 行为,它将 Value 限制在范围 <Minimum, Maximum-LargeChange +1>。Microsoft 的设计假设 LargeChange 等于您滚动的页面大小,因此当 Value 等于 Maximum - LargeChange +1 时,您仍然应该能够看到您正在滚动的页面的所有结束元素。也许这很有意义,但我更喜欢对 Maximum 的简单解释,并且 Maximum 与页面大小无关。ScrollBarEnhanced 可以从 Minimum 滚动到 Maximum(包括)。如果您更喜欢 Microsoft 工程师设想的默认 ScrollBar 行为,您可以随时修改源代码以满足您的需求。
  2. MinimumMaximum 都定义为 decimal 值。因此,理论上,它们都应该接受任何 decimal 值。虽然前一个陈述基本属实,但对 MaximumMinimum 还有一个额外的约束。Maximum - Minimum 的值不能大于 decimal.MaxValue。例如:
//Following are valid assignments for Minimum and Maximum
Minimum = 0;
Maximum  = decimal.MaxValue;
//Following is valid too
Minimum = deximal.MinValue/2+1;
Maximum = decimal.MaxValue/2;
//The following will throw OverflowException (because decimal.MaxValue+1>decimal.MaxValue)
Minimum = -1;
Maximum= decimal.MaxValue; 

演示程序

可下载的演示程序说明了如何在代码中使用 ScrollBarEnhanced 控件。它不需要太多解释。从程序的 GUI 中,您可以修改 ScrollBarEnhanced 控件实例的所有属性(滚动条显示在窗体的中央部分)。所有添加和修改的属性都分组在 Enhanced 类别下。我这样做只是为了让演示程序更容易导航,因为所有增强属性将并排显示在属性网格上。当然,您可以更改属性定义并将 category 属性更改为更合理的值。事实上,它们都应该属于 Behavior 类别。GUI 还捕获并显示 ScrollBarEnhanced 实例触发的所有增强事件。

可下载的 zip 文件还包含帮助文件(chm)。请注意,由于您从 Internet 下载此文件,为了使用它,您必须先取消阻止它。您可以从 Windows 资源管理器中执行此操作:右键单击帮助文件,选择“属性”,然后单击“取消阻止”按钮。

历史

2013 年 7 月 31 日 - 文章的初始版本。

2013 年 8 月 1 日 - 修复了源代码错误:由于 Brush 和 Pen 缓存,修改 Bookmark 的 Color 属性无效。

2013 年 8 月 3 日 - 一些更改

  • 删除了 BackgroundColor 属性。最初拥有它是个错误。
  • 添加了新的书签类型 ImageScrollBarBookmark - 允许显示微小图像(图标、位图)作为书签图形表示的新书签类型。
  • 添加了 QuickBookmarkNavigation 属性。它允许在单击书签时即时导航到书签值。

2013 年 8 月 6 日 - 错误修复和功能增强。

  • 错误修复:修复了 Maximum == LargeChange 时的除零异常。
  • 添加了 Orientation 属性。此属性增强了整个文章的范围。最初是关于垂直滚动条增强,现在它涵盖了垂直和水平滚动条功能。

2013 年 8 月 7 日 - 源代码更正,以修复最小值为零时绘图不正确的问​​题。我还更改了传递给 OnMouseClick 事件的 Value 参数的含义。以前它包含 ScrolBarEnhanced 实例的当前 Value。现在它是 HotValue(鼠标位置下的值)。

2013 年 8 月 9 日 - 更多添加和更改。

  • 添加了可自定义的上下文菜单,类似于 VScrollBarHScrollBar 的上下文菜单。
  • 增加了对鼠标滚轮移动的支持。
  • 添加了 BookmarksOnTop 布尔属性,用于控制滚动条元素的绘制顺序。
  • EnhancedMouseEventArgs 更改为继承自 MouseArgs,以向事件处理程序传递更详细的信息。

2013 年 8 月 12 日 - 添加了 ValueRangeScrollBarBookmark,允许基于值范围创建书签。

2013 年 9 月 17 日 - 下载文件更改:添加了帮助(chm);源代码更改:添加了注释,更正了 Dispose 覆盖。

2013 年 12 月 12 日 - 代码审查和清理。为设计模式添加了书签属性编辑器。

2014 年 10 月 2 日 - 错误修复;Scroll 事件的参数:EnhancedScrollEventArgs 显示的是 oldValue 而不是 newValue,反之亦然。该错误由 altie 发现 - altie 感谢您指出这一点。源代码已更新。

 

© . All rights reserved.