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

自定义绘制的滚动条

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.93/5 (22投票s)

2009 年 8 月 28 日

CPOL

3分钟阅读

viewsIcon

134739

downloadIcon

18190

一篇关于创建自定义滚动条控件的文章。

介绍 

我(在我的空闲时间)正在开发一个类似于 Outlook 2007 中日历控件的控件,并且我遇到了一个问题,即标准滚动条与我的日历控件的外观不太匹配。 所以我在 Web 和 CodeProject 上搜索了自定义绘制的滚动条。

除其他外,我发现了 Greg Ellis 的文章。 虽然它指引我走向了正确的方向,但我遗漏了系统滚动条能够做到的一些事情。 作为最后的手段,我在 Visual Studio 的帮助中搜索,并找到了一个自定义绘制滚动条的简单示例,我将其作为起点。

一些备注

此控件主要适用于构建自己的自定义绘制控件并需要滚动条的开发人员。

滚动条有一个主要的缺点 - 宽度是固定的。 我不需要此功能,并且绘制会更复杂,所以我将其省略了。
由于我使用了集合初始化器,该项目仅使用 C# 3.0 及以上版本的编译器构建,但这很容易修复。
为了使滚动条在按下并按住鼠标按钮时滚动,我使用了一个计时器 - 找不到其他方法。
绘制是使用 GDI+(在单独的渲染器类中)实现的,有两个例外:箭头按钮中的小箭头和拇指中的把手是图像 - 在那里,我感到羞耻,我有点懒惰。

控件的布局

正如所说,一张图片胜过千言万语,这里是一个包含基本类的图表:

请注意,属性 Opacity 设置上下文菜单的不透明度,而不是控件本身的不透明度。
我省略了控件设计器类,因为它不是真正必要的,仅在使用 Visual Studio 时才需要。

让我们开始

从头开始绘制控件时,有必要重写 OnPaint 方法,并且我认为,在构造函数中设置一些控件样式

public ScrollBarEx()
{
   // sets the control styles of the control
   SetStyle(
     ControlStyles.OptimizedDoubleBuffer | ControlStyles.ResizeRedraw
     | ControlStyles.Selectable | ControlStyles.AllPaintingInWmPaint
     | ControlStyles.UserPaint, true);

     ....
}

在那之后,需要考虑控件的逻辑,其中包括计算拇指大小和位置的方法。
例如,这里是计算拇指大小的方法:

private int GetThumbSize()
{
    // get the size of the track the thumb can occupy
    int trackSize =
       this.orientation == ScrollBarOrientation.Vertical ?
       this.Height - (2 * this.arrowHeight) : this.Width - (2 * this.arrowWidth);

    if (this.maximum == 0 || this.largeChange == 0)
    {
       return trackSize;
    }

    float newThumbSize = ((float)this.largeChange * (float)trackSize) /
      (float)this.maximum;

    return Convert.ToInt32(Math.Min((float)trackSize, Math.Max(newThumbSize, 10f)));
}

第二步是考虑鼠标处理,因此需要重写 OnMouseDownOnMouseUpOnMouseMove 方法以与鼠标交互。

鼠标处理主要涉及三个区域

  • 箭头按钮
  • 拇指
  • 拇指上方和下方的轨道

此外,我还重写了 OnMouseEnterOnMouseLeave 以获得关于鼠标是否位于滚动条上的视觉指示。

第三步是与键盘交互,这可以通过几种方式完成 - 一种方法是重写 ProcessDialogKey 方法,该方法还会捕获用于导航滚动条的箭头键。

最后一步是实现绘制功能,我将其放在一个渲染器类中,以分离控件的逻辑和绘制。

为了阻止绘制但仍然能够更新滚动条,向控件添加了两个方法:

  • BeginUpdate
  • EndUpdate

这些方法通过 SendMessage API 发送消息,切换绘制的开启或关闭。

一些最后的总结

这是我在 CodeProject(或其他地方)上的第一篇文章,所以请多多包涵。 我知道这篇文章不是很好,但我希望随着时间和经验的积累,未来的文章会更好。 请随时指出我应该改进的地方。

历史

  • 2009 年 8 月 28 日:初始发布
© . All rights reserved.