在WinForm应用程序开发中,一个富有视觉吸引力的用户界面往往能够提升用户体验。今天,我将为大家介绍一个如何自己手写一个的水波进度控件。
控件特性
- 流畅的水波动画效果
- 渐变色水波
- 可自定义的边框样式
- 阴影和高光效果
- 实时进度显示
核心实现
1. 基础架构
public class WaveProgressControl : Control
{
    private float _progress = 0f;
    private float _targetProgress = 0f;
    private Timer _animationTimer;
    private Timer _waveTimer;
    private float _waveOffset = 0f;
}
控件继承自基础Control类,通过两个Timer实现水波动画和进度变化的平滑过渡。
2. 视觉定制选项
控件提供了丰富的自定义选项,包括:
- 水波颜色(双色渐变)
- 边框样式(颜色、宽度、线型)
- 阴影效果
- 高光效果
- 文本颜色
3. 动画系统
private void InitializeTimers()
{
    _animationTimer = new Timer();
    _animationTimer.Interval = 16;
    _animationTimer.Tick += (s, e) =>
    {
        if (Math.Abs(_progress - _targetProgress) > 0.001f)
        {
            _progress += (_targetProgress - _progress) * ANIMATION_SPEED;
            Invalidate();
        }
    };
    _animationTimer.Start();
    _waveTimer = new Timer();
    _waveTimer.Interval = 16;
    _waveTimer.Tick += (s, e) =>
    {
        _waveOffset += 0.1f;
        if (_waveOffset > Math.PI * 2)
            _waveOffset = 0;
        Invalidate();
    };
    _waveTimer.Start();
}
采用双Timer机制:
- animationTimer负责进度值的平滑过渡
- waveTimer控制水波动画效果
4. 绘制过程
控件的绘制过程分为几个关键步骤:
阴影效果
// 绘制阴影效果  
if (EnableShade)
{
    using (GraphicsPath shadowPath = new GraphicsPath())
    {
        shadowPath.AddEllipse(circleRect);
        using (PathGradientBrush shadowBrush = new PathGradientBrush(shadowPath))
        {
            shadowBrush.CenterColor = ShadeColor;
            shadowBrush.SurroundColors = new Color[] { Color.Transparent };
            e.Graphics.FillPath(shadowBrush, shadowPath);
        }
    }
}
边框绘制
// 绘制外圆  
if (ShowBorder)
{
    using (GraphicsPath borderPath = new GraphicsPath())
    {
        borderPath.AddEllipse(circleRect);
        using (Pen borderPen = new Pen(BorderColor, BorderWidth))
        {
            borderPen.DashStyle = BorderStyle;
            e.Graphics.DrawPath(borderPen, borderPath);
        }
    }
}
水波效果
// 绘制水波  
int waveBottom = (int)(circleRect.Bottom - (circleRect.Height * currentProgress));
using (GraphicsPath wavePath = new GraphicsPath())
{
    Point[] points = new Point[circleRect.Width + 3];
    for (int x = 0; x <= circleRect.Width; x++)
    {
        int y = (int)(Math.Sin(x * 0.05f + _waveOffset) * WAVE_HEIGHT);
        points[x] = new Point(x + circleRect.X, waveBottom + y);
    }
    points[points.Length - 2] = new Point(circleRect.Right, circleRect.Bottom);
    points[points.Length - 1] = new Point(circleRect.Left, circleRect.Bottom);
    wavePath.AddLines(points);
    using (LinearGradientBrush brush = new LinearGradientBrush(
        circleRect,
        WaveColor1,
        WaveColor2,
        90f))
    {
        e.Graphics.FillPath(brush, wavePath);
    }
}
高光效果
// 绘制高光效果  
if (EnableHighlight)
{
    using (LinearGradientBrush highlightBrush = new LinearGradientBrush(
        circleRect,
        HighlightColor,
        Color.Transparent,
        45f))
    {
        e.Graphics.FillEllipse(highlightBrush, circleRect);
    }
}
完整代码
using System;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;
using Timer = System.Windows.Forms.Timer;
[ToolboxItem(true)]
[Description("水波浪进度控件")]
public class WaveProgressControl : Control
{
    private float _progress = 0f;
    private float _targetProgress = 0f;
    private Timer _animationTimer;
    private Timer _waveTimer;
    private float _waveOffset = 0f;
    private const float WAVE_HEIGHT = 10f;
    private const float ANIMATION_SPEED = 0.02f;
    #region 属性定义  
    // 基本属性  
    private Color _waveColor1 = Color.FromArgb(100, 181, 246);
    private Color _waveColor2 = Color.FromArgb(33, 150, 243);
    private Color _borderColor = Color.FromArgb(200, 200, 200);
    private float _borderWidth = 2f;
    private DashStyle _borderStyle = DashStyle.Solid;
    private bool _enableShade = true;
    private Color _shadeColor = Color.FromArgb(30, 0, 0, 0);
    private bool _enableHighlight = true;
    private Color _highlightColor = Color.FromArgb(30, 255, 255, 255);
    private Color _textColor = Color.FromArgb(64, 64, 64);
    private bool _showBorder = true;
    [Category("Appearance")]
    [Description("第一个波浪颜色")]
    public Color WaveColor1
    {
        get => _waveColor1;
        set
        {
            _waveColor1 = value;
            Invalidate();
        }
    }
    [Category("Appearance")]
    [Description("第二个波浪颜色")]
    public Color WaveColor2
    {
        get => _waveColor2;
        set
        {
            _waveColor2 = value;
            Invalidate();
        }
    }
    [Category("Border")]
    [Description("边框颜色")]
    public Color BorderColor
    {
        get => _borderColor;
        set
        {
            _borderColor = value;
            Invalidate();
        }
    }
    [Category("Border")]
    [Description("边框宽度")]
    public float BorderWidth
    {
        get => _borderWidth;
        set
        {
            _borderWidth = value;
            Invalidate();
        }
    }
    [Category("Border")]
    [Description("边框样式")]
    public DashStyle BorderStyle
    {
        get => _borderStyle;
        set
        {
            _borderStyle = value;
            Invalidate();
        }
    }
    [Category("Border")]
    [Description("是否显示边框")]
    public bool ShowBorder
    {
        get => _showBorder;
        set
        {
            _showBorder = value;
            Invalidate();
        }
    }
    [Category("Effects")]
    [Description("启用阴影效果")]
    public bool EnableShade
    {
        get => _enableShade;
        set
        {
            _enableShade = value;
            Invalidate();
        }
    }
    [Category("Effects")]
    [Description("阴影颜色")]
    public Color ShadeColor
    {
        get => _shadeColor;
        set
        {
            _shadeColor = value;
            Invalidate();
        }
    }
    [Category("Effects")]
    [Description("启用高光效果")]
    public bool EnableHighlight
    {
        get => _enableHighlight;
        set
        {
            _enableHighlight = value;
            Invalidate();
        }
    }
    [Category("Effects")]
    [Description("高光颜色")]
    public Color HighlightColor
    {
        get => _highlightColor;
        set
        {
            _highlightColor = value;
            Invalidate();
        }
    }
    [Category("Appearance")]
    [Description("文本颜色")]
    public Color TextColor
    {
        get => _textColor;
        set
        {
            _textColor = value;
            Invalidate();
        }
    }
    [Category("Behavior")]
    [Description("当前进度值(0-1)")]
    [DefaultValue(0f)]
    public float Progress
    {
        get => _targetProgress;
        set
        {
            if (value < 0f) value = 0f;
            if (value > 1f) value = 1f;
            _targetProgress = value;
        }
    }
    #endregion
    public WaveProgressControl()
    {
        Size = new Size(100, 100);
        SetStyle(ControlStyles.DoubleBuffer |
                ControlStyles.UserPaint |
                ControlStyles.AllPaintingInWmPaint |
                ControlStyles.OptimizedDoubleBuffer |
                ControlStyles.ResizeRedraw |
                ControlStyles.SupportsTransparentBackColor,
                true);
        if (!DesignMode)
        {
            InitializeTimers();
        }
    }
    private void InitializeTimers()
    {
        _animationTimer = new Timer();
        _animationTimer.Interval = 16;
        _animationTimer.Tick += (s, e) =>
        {
            if (Math.Abs(_progress - _targetProgress) > 0.001f)
            {
                _progress += (_targetProgress - _progress) * ANIMATION_SPEED;
                Invalidate();
            }
        };
        _animationTimer.Start();
        _waveTimer = new Timer();
        _waveTimer.Interval = 16;
        _waveTimer.Tick += (s, e) =>
        {
            _waveOffset += 0.1f;
            if (_waveOffset > Math.PI * 2)
                _waveOffset = 0;
            Invalidate();
        };
        _waveTimer.Start();
    }
    protected override void OnHandleCreated(EventArgs e)
    {
        base.OnHandleCreated(e);
        if (!DesignMode)
        {
            InitializeTimers();
        }
    }
    protected override void OnHandleDestroyed(EventArgs e)
    {
        if (!DesignMode)
        {
            _animationTimer?.Stop();
            _waveTimer?.Stop();
        }
        base.OnHandleDestroyed(e);
    }
    protected override void OnPaint(PaintEventArgs e)
    {
        e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
        e.Graphics.SmoothingMode = SmoothingMode.HighQuality;
        e.Graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
        e.Graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
        e.Graphics.CompositingQuality = CompositingQuality.HighQuality;
        int size = Math.Min(Width, Height);
        int padding = 10;
        Rectangle circleRect = new Rectangle(padding, padding, size - padding * 2, size - padding * 2);
        // 计算当前进度  
        float currentProgress = DesignMode ? Progress : _progress;
        // 绘制阴影效果  
        if (EnableShade)
        {
            using (GraphicsPath shadowPath = new GraphicsPath())
            {
                shadowPath.AddEllipse(circleRect);
                using (PathGradientBrush shadowBrush = new PathGradientBrush(shadowPath))
                {
                    shadowBrush.CenterColor = ShadeColor;
                    shadowBrush.SurroundColors = new Color[] { Color.Transparent };
                    e.Graphics.FillPath(shadowBrush, shadowPath);
                }
            }
        }
        // 绘制外圆  
        if (ShowBorder)
        {
            using (GraphicsPath borderPath = new GraphicsPath())
            {
                borderPath.AddEllipse(circleRect);
                using (Pen borderPen = new Pen(BorderColor, BorderWidth))
                {
                    borderPen.DashStyle = BorderStyle;
                    e.Graphics.DrawPath(borderPen, borderPath);
                }
            }
        }
        // 创建圆形裁剪区域  
        using (GraphicsPath clipPath = new GraphicsPath())
        {
            clipPath.AddEllipse(circleRect);
            e.Graphics.SetClip(clipPath);
            // 绘制水波  
            int waveBottom = (int)(circleRect.Bottom - (circleRect.Height * currentProgress));
            using (GraphicsPath wavePath = new GraphicsPath())
            {
                Point[] points = new Point[circleRect.Width + 3];
                for (int x = 0; x <= circleRect.Width; x++)
                {
                    int y = (int)(Math.Sin(x * 0.05f + _waveOffset) * WAVE_HEIGHT);
                    points[x] = new Point(x + circleRect.X, waveBottom + y);
                }
                points[points.Length - 2] = new Point(circleRect.Right, circleRect.Bottom);
                points[points.Length - 1] = new Point(circleRect.Left, circleRect.Bottom);
                wavePath.AddLines(points);
                using (LinearGradientBrush brush = new LinearGradientBrush(
                    circleRect,
                    WaveColor1,
                    WaveColor2,
                    90f))
                {
                    e.Graphics.FillPath(brush, wavePath);
                }
            }
            // 绘制高光效果  
            if (EnableHighlight)
            {
                using (LinearGradientBrush highlightBrush = new LinearGradientBrush(
                    circleRect,
                    HighlightColor,
                    Color.Transparent,
                    45f))
                {
                    e.Graphics.FillEllipse(highlightBrush, circleRect);
                }
            }
            e.Graphics.ResetClip();
        }
        // 绘制百分比文本  
        string percentText = $"{(currentProgress * 100):F0}%";
        using (Font font = new Font("Arial", size / 8))
        {
            SizeF textSize = e.Graphics.MeasureString(percentText, font);
            PointF textPos = new PointF(
                circleRect.X + (circleRect.Width - textSize.Width) / 2,
                circleRect.Y + (circleRect.Height - textSize.Height) / 2
            );
            using (Brush textBrush = new SolidBrush(TextColor))
            {
                e.Graphics.DrawString(percentText, font, textBrush, textPos);
            }
        }
        base.OnPaint(e);
    }
    protected override void Dispose(bool disposing)
    {
        if (disposing)
        {
            _animationTimer?.Dispose();
            _waveTimer?.Dispose();
        }
        base.Dispose(disposing);
    }
}
 
总结
这个水波进度控件不仅提供了醒目的视觉效果,还具有良好的可定制性和性能表现。它适用于需要展示进度信息的各种场景,能够为您的WinForm应用增添一份独特的视觉魅力。
该文章在 2024/11/22 17:04:11 编辑过