在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 编辑过