warning:本文默认您具有一定的基础知识,且对GDI+有一点了解。
虽然系统已经自带了滚动条控件,大部分情况下也能适用。但是有时候我们对界面进行整体美化的时候,自带的就显得有点傻大黑粗 了。。虽然我们也完全可以在原有控件的基础上进行美化,但是本文更侧重学习新知识。故此本文决定重复造轮子。
首先在项目右键选择添加新项(名字可能不同,相信您能找到正确的按钮),然后选择自定义控件,注意是自定义控件而不是用户组件。并重命名控件名称。
然后复制下方代码
public partial class ScrollBar : Control
{
private int minimum = 0;
private int maximum = 100;
private int largeChange = 50;
private int smallChange = 1;
private int scrollValue = 0;
private Rectangle ScrollBannerRectangle;
private int bannerWidth;
private double bl;
private int MaxValue = 51;
[Category("自定义事件")]
[Description("用户滚动滑块时触发此事件")]
[Browsable(true)]
public event EventHandler<ScrollEventArgs> Scroll;
[Category("自定义事件")]
[Description("滚动条滑块的值改变时触发此事件")]
[Browsable(true)]
public event EventHandler ValueChanged;
[Category("自定义事件")]
[Description("鼠标点击滑动时触发")]
[Browsable(true)]
public event EventHandler MouseDownBanner;
[Category("自定义事件")]
[Description("鼠标松开滑动时触发")]
[Browsable(true)]
public event EventHandler MouseUpBanner;
public class ScrollEventArgs : EventArgs
{
public int NewValue = 0;
public int OldValue = 0;
}
bool mouseHover = false;
//D2d d2D;
public ScrollBar()
{
SetStyle(ControlStyles.UserPaint, true);
SetStyle(ControlStyles.ResizeRedraw, true);
SetStyle(ControlStyles.Selectable, false);
SetStyle(ControlStyles.AllPaintingInWmPaint, true);
SetStyle(ControlStyles.SupportsTransparentBackColor, true);
SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
InitializeComponent();
CalcScrollBannerRectangle(true);
}
~ScrollBar()
{
// d2D?.Dispose();
}
[Browsable(true)]
public int Minimum
{
get => minimum;
set
{
if (minimum == value)
return;
minimum = value;
CalcScrollBannerRectangle(true);
Invalidate();
}
}
[Browsable(true)]
public int Maximum
{
get => maximum;
set
{
if (maximum == value)
return;
maximum = value;
CalcScrollBannerRectangle(true);
Invalidate();
}
}
[Browsable(true)]
public int LargeChange
{
get => largeChange;
set
{
largeChange = value;
CalcScrollBannerRectangle(true);
Invalidate();
}
}
[Browsable(true)]
public int SmallChange
{
get => smallChange;
set
{
if (smallChange == value)
return;
smallChange = value;
CalcScrollBannerRectangle(true);
Invalidate();
//DoPaint();
}
}
[Browsable(true)]
public int Value
{
get => scrollValue;
set
{
if (value < Minimum)
value = Minimum;
else if (value > MaxValue)
value = MaxValue;
if (scrollValue == value)
return;
this.scrollValue = value;
scrollTop = (int)(value * bl);
CalcScrollBannerRectangle();
Invalidate();
ValueChanged?.Invoke(this, EventArgs.Empty);
}
}
protected override void OnVisibleChanged(EventArgs e)
{
base.OnVisibleChanged(e);
Invalidate();
}
protected override void OnResize(EventArgs e)
{
base.OnResize(e);
CalcScrollBannerRectangle(true);
}
protected void CalcScrollBannerRectangle()
{
CalcScrollBannerRectangle(false);
}
protected void CalcScrollBannerRectangle(bool calcScrollTop)
{
MaxValue = Maximum - LargeChange + 1 + Minimum;
bl = (Height - Padding.Top - Padding.Bottom) / (double)(Maximum - Minimum + 1);
int bannerHeight = (int)(LargeChange * bl);
bannerWidth = Width - 2;
if (calcScrollTop)
scrollTop = Convert.ToInt32(Value * bl);
ScrollBannerRectangle = new Rectangle(0,
Padding.Top + scrollTop,
bannerWidth, bannerHeight);
}
protected MouseButtons mouseButtonsState = 0;
protected bool mouseDownOnBanner = false;
protected bool IsMouseDown = false;
protected override void OnMouseUp(MouseEventArgs e)
{
base.OnMouseUp(e);
if (e.Button == MouseButtons.Left)
{
if (mouseDownOnBanner)
{
mouseDownOnBanner = false;
MouseUpBanner?.Invoke(this, EventArgs.Empty);
}
IsMouseDown = false;
Invalidate();
}
}
Point mouseDownPoint = new Point(0, 0);
int oldValue = 0;
protected override void OnMouseEnter(EventArgs e)
{
base.OnMouseEnter(e);
Invalidate();
}
protected override void OnMouseLeave(EventArgs e)
{
base.OnMouseLeave(e);
if (mouseHover)
{
mouseHover = false;
Invalidate();
}
}
protected void ScrollEvent(int oldValue, int newValue)
{
Scroll?.Invoke(this, new ScrollEventArgs { OldValue = oldValue, NewValue = newValue });
}
protected override void OnMouseDown(MouseEventArgs e)
{
base.OnMouseDown(e);
if (e.Button == MouseButtons.Left)
{
/*
* 如果当前鼠标左键点击的区域不在滚动条内
* 则将滚动条(中心点)滚动至鼠标点击位置
*/
if (!ScrollBannerRectangle.Contains(e.X, e.Y))
{
int top = e.Y - (ScrollBannerRectangle.Height / 2) - Padding.Top;
if (top < 0)
top = 0;
else if (top > Height - Padding.Top - ScrollBannerRectangle.Height)
top = Height - Padding.Top - ScrollBannerRectangle.Height;
int newValue = Convert.ToInt32(top / bl);
ScrollEvent(scrollValue, newValue);
Value = newValue;
}
mouseDownPoint.X = e.X;
mouseDownPoint.Y = e.Y;
oldValue = scrollTop;
IsMouseDown = true;
mouseDownOnBanner = true;
MouseDownBanner?.Invoke(this, EventArgs.Empty);
}
Invalidate();
}
int scrollTop = 0;
protected override void OnMouseMove(MouseEventArgs e)
{
base.OnMouseMove(e);
bool hitTest = ScrollBannerRectangle.Contains(e.X, e.Y);
if (mouseDownOnBanner)
{
scrollTop = oldValue + e.Y - mouseDownPoint.Y;
int v;
if (scrollTop < 0)
{
v = 0;
scrollTop = 0;
}
else if (scrollTop > MaxValue * bl)
{
v = MaxValue;
scrollTop = Convert.ToInt32(MaxValue * bl);
}
else
{
v = Convert.ToInt32(scrollTop / bl);
}
if (scrollValue != v)
{
// Console.WriteLine(v);
Scroll?.Invoke(this, new ScrollEventArgs { OldValue = scrollValue, NewValue = v });
scrollValue = v;
ValueChanged?.Invoke(this, EventArgs.Empty);
}
CalcScrollBannerRectangle();
Invalidate();
//Update用于立刻更新界面,否则有可能造成闪烁
Update();
}
else if (!mouseHover && hitTest)
{
mouseHover = true;
Invalidate();
}
else if (mouseHover && !hitTest)
{
mouseHover = false;
Invalidate();
}
}
protected override void OnPaintBackground(PaintEventArgs pevent)
{
return;
}
protected void OnPaintBackground(Graphics graphics)
{
graphics.Clear(Parent.BackColor);
}
protected virtual void OnPaintScrollBanner(Graphics graphics)
{
if (!Enabled)
return;
Color bannerColor;
if (IsMouseDown)
{
bannerColor = Color.FromArgb(223, 74, 22);
}
else if (mouseHover)
{
bannerColor = Color.FromArgb(192, 192, 192);
}
else
{
bannerColor = Color.FromArgb(255, 255, 255);
}
using (SolidBrush solidBrush = new SolidBrush(bannerColor))
using (GraphicsPath graphicsPath = new GraphicsPath())
{
graphicsPath.StartFigure();
graphicsPath.AddArc(
ScrollBannerRectangle.Left,
ScrollBannerRectangle.Top,
ScrollBannerRectangle.Width,
ScrollBannerRectangle.Width,
180, 180);
graphicsPath.AddLine(ScrollBannerRectangle.Right,
ScrollBannerRectangle.Top + ScrollBannerRectangle.Width / 2,
ScrollBannerRectangle.Right,
ScrollBannerRectangle.Bottom - ScrollBannerRectangle.Width / 2
);
graphicsPath.AddArc(
ScrollBannerRectangle.Left,
ScrollBannerRectangle.Bottom - ScrollBannerRectangle.Width,
ScrollBannerRectangle.Width,
ScrollBannerRectangle.Width,
0, 180);
graphicsPath.AddLine(ScrollBannerRectangle.Left,
ScrollBannerRectangle.Top + ScrollBannerRectangle.Width / 2,
ScrollBannerRectangle.Left,
ScrollBannerRectangle.Bottom - ScrollBannerRectangle.Width / 2
);
graphicsPath.CloseFigure();
graphics.FillPath(solidBrush, graphicsPath);
}
/// */
}
protected override void OnPaint(PaintEventArgs pe)
{
base.OnPaint(pe);
pe.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
OnPaintBackground(pe.Graphics);
OnPaintScrollBanner(pe.Graphics);
}
}
几乎与自带的滚动条使用方法是一样的。可以直接拖动使用或者使用编程的方式:
ScrollBar = new ScrollBar();
//...
//设置滚动条的Dock Width 之类的属性
//...
scrollBar.Minimum = 0;
scrollBar.Maximum = 100;
scrollBar.LargeChange = 1
scrollBar.Value = 2;