RangeSlider是一个自定义的Windows Forms控件,允许用户通过两个滑块来选择一个数值范围。这种控件在需要设置数值范围的场景中非常实用,比如价格区间筛选、年龄范围选择等。
本文将详细介绍如何实现一个具有以下特性的RangeSlider控件:
让我们看看这个控件的效果:
C#using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing.Drawing2D;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace AppControls
{
public class RangeSlider : Control
{
#region 私有字段
private int _minimum = 0;
private int _maximum = 100;
private int _lowerValue = 0;
private int _upperValue = 100;
private Rectangle _trackRectangle;
private Rectangle _lowerThumbRect;
private Rectangle _upperThumbRect;
private bool _isDraggingLower;
private bool _isDraggingUpper;
private const int THUMB_SIZE = 16;
private int _trackHeight = 4;
private Color _trackColor = Color.LightGray;
private Color _selectedTrackColor = Color.RoyalBlue;
#endregion
#region 属性
[Description("最小可选值")]
public int Minimum
{
get => _minimum;
set
{
if (value >= _maximum) return;
_minimum = value;
if (_lowerValue < _minimum)
LowerValue = _minimum;
Invalidate();
}
}
[Description("最大可选值")]
public int Maximum
{
get => _maximum;
set
{
if (value <= _minimum) return;
_maximum = value;
if (_upperValue > _maximum)
UpperValue = _maximum;
Invalidate();
}
}
[Description("当前选择的最小值")]
public int LowerValue
{
get => _lowerValue;
set
{
if (value < _minimum || value > _upperValue) return;
if (_lowerValue != value)
{
_lowerValue = value;
OnValueChanged(EventArgs.Empty);
Invalidate();
}
}
}
[Description("当前选择的最大值")]
public int UpperValue
{
get => _upperValue;
set
{
if (value > _maximum || value < _lowerValue) return;
if (_upperValue != value)
{
_upperValue = value;
OnValueChanged(EventArgs.Empty);
Invalidate();
}
}
}
[Description("轨道高度")]
[DefaultValue(4)]
public int TrackHeight
{
get => _trackHeight;
set
{
if (value < 1) return;
if (_trackHeight != value)
{
_trackHeight = value;
Invalidate();
}
}
}
[Description("轨道背景颜色")]
public Color TrackColor
{
get => _trackColor;
set
{
if (_trackColor != value)
{
_trackColor = value;
Invalidate();
}
}
}
[Description("选中区域轨道颜色")]
public Color SelectedTrackColor
{
get => _selectedTrackColor;
set
{
if (_selectedTrackColor != value)
{
_selectedTrackColor = value;
Invalidate();
}
}
}
#endregion
#region 事件
public event EventHandler ValueChanged;
protected virtual void OnValueChanged(EventArgs e)
{
ValueChanged?.Invoke(this, e);
}
#endregion
#region 构造函数
public RangeSlider()
{
SetStyle(ControlStyles.AllPaintingInWmPaint |
ControlStyles.UserPaint |
ControlStyles.OptimizedDoubleBuffer |
ControlStyles.ResizeRedraw, true);
MinimumSize = new Size(100, THUMB_SIZE + 4);
}
#endregion
#region 重写方法
protected override void OnPaint(PaintEventArgs e)
{
var g = e.Graphics;
g.SmoothingMode = SmoothingMode.AntiAlias;
// 计算轨道区域,使用自定义高度
_trackRectangle = new Rectangle(
THUMB_SIZE / 2,
Height / 2 - _trackHeight / 2, // 居中显示
Width - THUMB_SIZE,
_trackHeight);
// 绘制背景轨道,使用自定义颜色
using (var trackBrush = new SolidBrush(_trackColor))
{
// 使用圆角矩形绘制轨道
using (var path = CreateRoundedRectangle(_trackRectangle, _trackHeight / 2))
{
g.FillPath(trackBrush, path);
}
}
// 计算滑块位置
int lowerX = ValueToPoint(_lowerValue);
int upperX = ValueToPoint(_upperValue);
// 绘制选中区域,使用自定义颜色
using (var selectedBrush = new SolidBrush(_selectedTrackColor))
{
var selectedRect = new Rectangle(
lowerX,
_trackRectangle.Y,
upperX - lowerX,
_trackRectangle.Height);
// 使用圆角矩形绘制选中区域
using (var path = CreateRoundedRectangle(selectedRect, _trackHeight / 2))
{
g.FillPath(selectedBrush, path);
}
}
// 计算滑块矩形
_lowerThumbRect = new Rectangle(
lowerX - THUMB_SIZE / 2,
Height / 2 - THUMB_SIZE / 2,
THUMB_SIZE,
THUMB_SIZE);
_upperThumbRect = new Rectangle(
upperX - THUMB_SIZE / 2,
Height / 2 - THUMB_SIZE / 2,
THUMB_SIZE,
THUMB_SIZE);
// 绘制滑块
DrawThumb(g, _lowerThumbRect);
DrawThumb(g, _upperThumbRect);
}
// 创建圆角矩形路径
private GraphicsPath CreateRoundedRectangle(Rectangle rect, int radius)
{
GraphicsPath path = new GraphicsPath();
if (radius > 0)
{
int diameter = radius * 2;
Rectangle arc = new Rectangle(rect.Location, new Size(diameter, diameter));
// 左上角
path.AddArc(arc, 180, 90);
// 上边
arc.X = rect.Right - diameter;
path.AddArc(arc, 270, 90);
// 右边
arc.Y = rect.Bottom - diameter;
path.AddArc(arc, 0, 90);
// 下边
arc.X = rect.Left;
path.AddArc(arc, 90, 90);
path.CloseFigure();
}
else
{
path.AddRectangle(rect);
}
return path;
}
protected override void OnMouseDown(MouseEventArgs e)
{
if (_lowerThumbRect.Contains(e.Location))
{
_isDraggingLower = true;
}
else if (_upperThumbRect.Contains(e.Location))
{
_isDraggingUpper = true;
}
base.OnMouseDown(e);
}
protected override void OnMouseMove(MouseEventArgs e)
{
if (_isDraggingLower)
{
LowerValue = PointToValue(e.X);
}
else if (_isDraggingUpper)
{
UpperValue = PointToValue(e.X);
}
base.OnMouseMove(e);
}
protected override void OnMouseUp(MouseEventArgs e)
{
_isDraggingLower = false;
_isDraggingUpper = false;
base.OnMouseUp(e);
}
#endregion
#region 辅助方法
private void DrawThumb(Graphics g, Rectangle rect)
{
using (var path = new GraphicsPath())
{
path.AddEllipse(rect);
using (var brush = new SolidBrush(Color.White))
{
g.FillPath(brush, path);
}
using (var pen = new Pen(Color.DarkGray))
{
g.DrawPath(pen, path);
}
}
}
private int ValueToPoint(int value)
{
if (_maximum == _minimum)
return _trackRectangle.X;
return _trackRectangle.X + (int)((value - _minimum) * _trackRectangle.Width /
(_maximum - _minimum));
}
private int PointToValue(int x)
{
if (x < _trackRectangle.X)
return _minimum;
if (x > _trackRectangle.Right)
return _maximum;
return _minimum + (x - _trackRectangle.X) * (_maximum - _minimum) /
_trackRectangle.Width;
}
#endregion
}
}
这个自定义的RangeSlider控件实现了一个美观、实用的范围选择功能,可以很方便地集成到Windows Forms应用程序中。通过属性设置,可以灵活地调整控件的外观和行为,满足不同场景的需求。
该控件的实现涉及了GDI+绘图、事件处理、属性设计等多个方面,是一个很好的自定义控件开发示例。希望这个实现能对你的开发工作有所帮助!
本文作者:技术老小子
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!