编辑
2025-02-12
C# 应用
00
请注意,本文编写于 83 天前,最后修改于 83 天前,其中某些信息可能已经过时。

目录

控件特性概述
核心实现原理
基础架构
动画系统
边框系统
绘制技术详解
进度条绘制
文本渘染
属性系统设计
完整代码
总结

在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)}%";

属性系统设计

控件提供了丰富的可配置属性:

  • BorderColor:边框颜色
  • BorderWidth:边框宽度
  • BorderStyle:边框样式
  • IsUpward:进度方向
  • ProgressColor:进度条颜色
  • Value:进度值
  • CurrentValue:当前实际值(只读)

每个属性都添加了适当的特性标记,支持设计器中的配置:

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)); } } } }

image.png

总结

这个垂直进度条控件展示了如何将GDI+技术应用于实际开发中。通过精心的设计和优化,我们实现了一个既美观又实用的控件,它可以轻松集成到任何Windows Forms应用程序中。

核心要点:

  1. 合理的架构设计
  2. 平滑的动画效果
  3. 精确的绘制计算
  4. 丰富的自定义选项
  5. 良好的性能优化

这个控件不仅可以直接使用,还可以作为学习GDI+和自定义控件开发的极好示例。

本文作者:rick

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!