在Windows Forms开发中,有时候我们需要一些特殊的自定义控件来满足项目的需求。本文将详细介绍如何使用GDI+技术开发一个专业级的垂直进度条控件,它不仅功能强大,而且具有出色的视觉效果。
这个垂直进度条控件具有以下突出特点:
控件继承自Control
类,通过重写OnPaint
方法实现自定义绘制。为了确保绘制性能,启用了以下绘制选项:
C#SetStyle(ControlStyles.UserPaint |
ControlStyles.AllPaintingInWmPaint |
ControlStyles.OptimizedDoubleBuffer, true);
采用Timer实现平滑动画效果,关键参数设置:
C#private const double ANIMATION_PRECISION = 0.01;
private const double ANIMATION_SPEED = 1.0;
private Timer _animationTimer;
动画计算采用线性插值方式,保证了过渡的流畅性:
C#double diff = _targetValue - _value;
double step = Math.Sign(diff) * Math.Min(Math.Abs(diff), ANIMATION_SPEED);
_value += step;
设计了丰富的边框样式枚举:
C#public enum BorderStyles
{
None,
Solid,
Dash,
Dot,
DashDot,
DashDotDot,
Custom
}
边框绘制考虑了线宽偏移,确保边框显示准确:
C#Rectangle borderRect = new Rectangle(
_borderWidth / 2,
_borderWidth / 2,
Width - _borderWidth,
Height - _borderWidth
);
进度条采用渐变填充,提供更好的视觉效果:
C#using (var gradientBrush = new LinearGradientBrush(
new Point(contentRect.Left, 0),
new Point(contentRect.Right, 0),
Color.FromArgb(200, _progressColor),
_progressColor))
{
g.FillRectangle(gradientBrush, ...);
}
百分比文本采用抗锯齿渲染,确保清晰度:
C#g.SmoothingMode = SmoothingMode.AntiAlias;
string percentText = $"{Math.Round(_value, 1)}%";
控件提供了丰富的可配置属性:
每个属性都添加了适当的特性标记,支持设计器中的配置:
C#[Category("Appearance")]
[Description("边框颜色")]
public Color BorderColor
{
get => _borderColor;
set
{
_borderColor = value;
Invalidate();
}
}
C#using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing.Drawing2D;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Timer = System.Windows.Forms.Timer;
namespace AppControls
{
public class VerticalProgressBar : Control
{
private double _value = 0;
private double _targetValue = 0;
private bool _isUpward = true;
private Timer _animationTimer;
private Color _progressColor = Color.FromArgb(0, 122, 204);
private Color _backgroundColor = Color.FromArgb(240, 240, 240);
private Color _borderColor = Color.Gray;
private int _borderWidth = 1;
private BorderStyles _borderStyle = BorderStyles.Solid;
// 添加动画精度控制
private const double ANIMATION_PRECISION = 0.01;
private const double ANIMATION_SPEED = 1.0;
public enum BorderStyles
{
None,
Solid,
Dash,
Dot,
DashDot,
DashDotDot,
Custom
}
public VerticalProgressBar()
{
SetStyle(ControlStyles.UserPaint |
ControlStyles.AllPaintingInWmPaint |
ControlStyles.OptimizedDoubleBuffer, true);
_animationTimer = new Timer();
_animationTimer.Interval = 16; // 约60 FPS
_animationTimer.Tick += AnimationTimer_Tick;
}
#region 属性
[Category("Appearance")]
[Description("边框颜色")]
public Color BorderColor
{
get => _borderColor;
set
{
_borderColor = value;
Invalidate();
}
}
[Category("Appearance")]
[Description("边框宽度")]
public int BorderWidth
{
get => _borderWidth;
set
{
if (value < 0) value = 0;
_borderWidth = value;
Invalidate();
}
}
[Category("Appearance")]
[Description("边框样式")]
public BorderStyles BorderStyle
{
get => _borderStyle;
set
{
_borderStyle = value;
Invalidate();
}
}
[Category("Appearance")]
public bool IsUpward
{
get => _isUpward;
set
{
_isUpward = value;
Invalidate();
}
}
[Category("Appearance")]
public Color ProgressColor
{
get => _progressColor;
set
{
_progressColor = value;
Invalidate();
}
}
[Category("Appearance")]
public double Value
{
get => _targetValue;
set
{
value = Math.Max(0, Math.Min(100, value));
if (Math.Abs(_targetValue - value) > ANIMATION_PRECISION)
{
_targetValue = value;
_animationTimer.Start();
}
}
}
// 添加当前值的只读属性
[Browsable(false)]
public double CurrentValue => Math.Round(_value, 2);
#endregion
private void AnimationTimer_Tick(object sender, EventArgs e)
{
double diff = _targetValue - _value;
// 如果差值小于精度阈值,直接设置为目标值
if (Math.Abs(diff) < ANIMATION_PRECISION)
{
_value = _targetValue;
_animationTimer.Stop();
Invalidate();
return;
}
// 计算每一步的变化量
double step = Math.Sign(diff) * Math.Min(Math.Abs(diff), ANIMATION_SPEED);
_value += step;
// 确保值在有效范围内
_value = Math.Max(0, Math.Min(100, _value));
Invalidate();
}
private DashStyle GetDashStyle(BorderStyles style)
{
return style switch
{
BorderStyles.Solid => DashStyle.Solid,
BorderStyles.Dash => DashStyle.Dash,
BorderStyles.Dot => DashStyle.Dot,
BorderStyles.DashDot => DashStyle.DashDot,
BorderStyles.DashDotDot => DashStyle.DashDotDot,
_ => DashStyle.Solid
};
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
Graphics g = e.Graphics;
g.SmoothingMode = SmoothingMode.AntiAlias;
// 计算边框内的实际绘制区域
Rectangle contentRect = new Rectangle(
_borderWidth,
_borderWidth,
Width - 2 * _borderWidth,
Height - 2 * _borderWidth
);
// 绘制背景
using (var brush = new SolidBrush(_backgroundColor))
{
g.FillRectangle(brush, contentRect);
}
// 精确计算进度条高度,避免舍入误差
double progressPercentage = _value / 100.0;
int progressHeight = (int)Math.Round(contentRect.Height * progressPercentage);
// 创建渐变画刷
using (var gradientBrush = new LinearGradientBrush(
new Point(contentRect.Left, 0),
new Point(contentRect.Right, 0),
Color.FromArgb(200, _progressColor),
_progressColor))
{
// 根据方向绘制进度条
if (_isUpward)
{
g.FillRectangle(gradientBrush,
contentRect.X,
contentRect.Bottom - progressHeight,
contentRect.Width,
progressHeight);
}
else
{
g.FillRectangle(gradientBrush,
contentRect.X,
contentRect.Y,
contentRect.Width,
progressHeight);
}
}
// 绘制边框
if (_borderStyle != BorderStyles.None && _borderWidth > 0)
{
using (var pen = new Pen(_borderColor, _borderWidth))
{
pen.DashStyle = GetDashStyle(_borderStyle);
if (_borderStyle == BorderStyles.Custom)
{
pen.DashPattern = new float[] { 3.0F, 2.0F, 1.0F, 2.0F };
}
Rectangle borderRect = new Rectangle(
_borderWidth / 2,
_borderWidth / 2,
Width - _borderWidth,
Height - _borderWidth
);
g.DrawRectangle(pen, borderRect);
}
}
// 绘制百分比文字,使用固定的小数位数
string percentText = $"{Math.Round(_value, 1)}%";
using (var textBrush = new SolidBrush(ForeColor))
using (var font = new Font("Arial", 9f, FontStyle.Bold))
{
var textSize = g.MeasureString(percentText, font);
g.DrawString(percentText, font, textBrush,
new PointF((Width - textSize.Width) / 2, 5 + _borderWidth));
}
}
}
}
这个垂直进度条控件展示了如何将GDI+技术应用于实际开发中。通过精心的设计和优化,我们实现了一个既美观又实用的控件,它可以轻松集成到任何Windows Forms应用程序中。
核心要点:
这个控件不仅可以直接使用,还可以作为学习GDI+和自定义控件开发的极好示例。
本文作者:rick
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!