本文将详细介绍如何使用C#和GDI+技术实现一个自定义的线性表控件(Linear Gauge)。这个控件具有清晰的刻度显示、数值指示和专业的外观,适用于各种仪表盘和监控界面。
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 LinearGauge : Control
{
// 私有字段
private float _minimum = 0;
private float _maximum = 100;
private float _value = 0;
private Color _scaleColor = Color.FromArgb(40, 40, 40);
private Color _valueColor = Color.FromArgb(220, 50, 50);
private Color _glassColor = Color.FromArgb(200, 210, 230);
private Color _borderColor = Color.FromArgb(120, 120, 120);
private int _majorTickCount = 5;
private int _minorTickCount = 5;
private int _cornerRadius = 10;
private float _borderWidth = 1.5f;
private bool _enableRoundedCorners = true;
private Timer animationTimer;
private float currentAnimatedValue;
private float targetValue;
private float animationStep;
private int animationDuration = 500; // 默认动画持续时间(毫秒)
private int animationInterval = 16; // 约60fps
private bool isAnimating = false;
private AnimationMode animationMode = AnimationMode.EaseInOut;
// 动画模式枚举
public enum AnimationMode
{
Linear,
EaseIn,
EaseOut,
EaseInOut
}
// 动画完成事件
public event EventHandler AnimationCompleted;
[Description("动画持续时间(毫秒)")]
public int AnimationDuration
{
get => animationDuration;
set
{
animationDuration = Math.Max(0, value);
CalculateAnimationStep(_value);
}
}
[Description("动画模式")]
public AnimationMode AnimationType
{
get => animationMode;
set
{
animationMode = value;
Invalidate();
}
}
[Description("是否正在动画")]
public bool IsAnimating => isAnimating;
// 属性
[Description("最小值")]
public float Minimum
{
get => _minimum;
set
{
_minimum = value;
Invalidate();
}
}
[Description("最大值")]
public float Maximum
{
get => _maximum;
set
{
_maximum = value;
Invalidate();
}
}
[Description("当前值")]
public float Value
{
get => _value;
set
{
if (_value != value)
{
if (animationDuration > 0 && IsHandleCreated)
{
StartAnimation(value);
}
else
{
_value = Math.Max(Minimum, Math.Min(Maximum, value));
currentAnimatedValue = _value;
Invalidate();
}
}
}
}
[Description("玻璃效果颜色")]
public Color GlassColor
{
get => _glassColor;
set
{
_glassColor = value;
Invalidate();
}
}
[Description("边框颜色")]
public Color BorderColor
{
get => _borderColor;
set
{
_borderColor = value;
Invalidate();
}
}
[Description("边框宽度")]
public float BorderWidth
{
get => _borderWidth;
set
{
_borderWidth = value;
Invalidate();
}
}
[Description("是否启用圆角")]
public bool EnableRoundedCorners
{
get => _enableRoundedCorners;
set
{
_enableRoundedCorners = value;
Invalidate();
}
}
[Description("圆角半径")]
public int CornerRadius
{
get => _cornerRadius;
set
{
_cornerRadius = Math.Max(0, Math.Min(value, Math.Min(Width, Height) / 2));
Invalidate();
}
}
// 构造函数
public LinearGauge()
{
SetStyle(ControlStyles.DoubleBuffer |
ControlStyles.AllPaintingInWmPaint |
ControlStyles.UserPaint |
ControlStyles.ResizeRedraw, true);
Size = new Size(80, 300);
BackColor = Color.FromArgb(240, 240, 240);
// 初始化动画计时器
animationTimer = new Timer();
animationTimer.Interval = animationInterval;
animationTimer.Tick += AnimationTimer_Tick;
// 初始化动画值
currentAnimatedValue = _value;
targetValue = _value;
}
// 启动动画
private void StartAnimation(float newValue)
{
if (IsDisposed || !IsHandleCreated) return;
targetValue = Math.Max(Minimum, Math.Min(Maximum, newValue));
if (Math.Abs(targetValue - currentAnimatedValue) < float.Epsilon)
{
return;
}
// 计算动画步长
float totalDistance = targetValue - currentAnimatedValue;
float totalSteps = animationDuration / animationInterval;
animationStep = totalDistance / totalSteps;
isAnimating = true;
animationTimer.Start();
}
// 动画计时器事件处理
private void AnimationTimer_Tick(object sender, EventArgs e)
{
if (!isAnimating) return;
float previousValue = currentAnimatedValue;
float progress = Math.Abs((currentAnimatedValue - _value) / (targetValue - _value));
// 根据动画模式计算新值
float step = CalculateAnimationStep(progress);
currentAnimatedValue += step;
// 检查是否到达目标值
if ((step > 0 && currentAnimatedValue >= targetValue) ||
(step < 0 && currentAnimatedValue <= targetValue))
{
currentAnimatedValue = targetValue;
_value = targetValue;
isAnimating = false;
animationTimer.Stop();
AnimationCompleted?.Invoke(this, EventArgs.Empty);
}
// 当值改变时才重绘
if (Math.Abs(previousValue - currentAnimatedValue) > float.Epsilon)
{
Invalidate();
}
}
// 计算动画步长
private float CalculateAnimationStep(float progress)
{
float step = animationStep;
switch (animationMode)
{
case AnimationMode.Linear:
// 线性动画,步长保持不变
break;
case AnimationMode.EaseIn:
// 缓入动画,开始慢后来快
step *= (progress + 0.5f);
break;
case AnimationMode.EaseOut:
// 缓出动画,开始快后来慢
step *= (1.5f - progress);
break;
case AnimationMode.EaseInOut:
// 缓入缓出动画,开始和结束慢,中间快
if (progress < 0.5f)
step *= (progress + 0.5f);
else
step *= (1.5f - progress);
break;
}
return step;
}
// 创建圆角路径
private GraphicsPath CreateRoundedRectangle(Rectangle bounds, int radius)
{
if (!EnableRoundedCorners || radius <= 0)
{
GraphicsPath path = new GraphicsPath();
path.AddRectangle(bounds);
return path;
}
radius = Math.Min(radius, Math.Min(bounds.Width, bounds.Height) / 2);
GraphicsPath roundedPath = new GraphicsPath();
int diameter = radius * 2;
Rectangle arc = new Rectangle(bounds.Location, new Size(diameter, diameter));
// 左上角
roundedPath.AddArc(arc, 180, 90);
// 右上角
arc.X = bounds.Right - diameter;
roundedPath.AddArc(arc, 270, 90);
// 右下角
arc.Y = bounds.Bottom - diameter;
roundedPath.AddArc(arc, 0, 90);
// 左下角
arc.X = bounds.Left;
roundedPath.AddArc(arc, 90, 90);
roundedPath.CloseFigure();
return roundedPath;
}
// 重写OnPaint方法
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
Graphics g = e.Graphics;
g.SmoothingMode = SmoothingMode.AntiAlias;
// 计算绘制区域,考虑边框宽度
Rectangle gaugeRect = new Rectangle(
Padding.Left + (int)Math.Ceiling(_borderWidth / 2),
Padding.Top + (int)Math.Ceiling(_borderWidth / 2),
Width - Padding.Horizontal - (int)Math.Ceiling(_borderWidth),
Height - Padding.Vertical - (int)Math.Ceiling(_borderWidth)
);
// 创建路径
using (GraphicsPath path = CreateRoundedRectangle(gaugeRect, _enableRoundedCorners ? _cornerRadius : 0))
{
// 绘制玻璃效果背景
DrawGlassEffect(g, path, gaugeRect);
// 计算刻度区域
int scaleMargin = 25;
Rectangle scaleRect = new Rectangle(
gaugeRect.Left + scaleMargin,
gaugeRect.Top + 10,
10,
gaugeRect.Height - 20
);
// 绘制刻度线和刻度值
DrawScale(g, scaleRect);
// 绘制当前值指示器
DrawValueIndicator(g, scaleRect);
// 绘制边框
using (Pen borderPen = new Pen(_borderColor, _borderWidth))
{
// 设置边框连接和端点样式
borderPen.LineJoin = LineJoin.Round;
borderPen.StartCap = LineCap.Round;
borderPen.EndCap = LineCap.Round;
g.DrawPath(borderPen, path);
}
}
}
// 绘制玻璃效果
private void DrawGlassEffect(Graphics g, GraphicsPath path, Rectangle bounds)
{
// 主背景 - 使用更细腻的渐变
using (LinearGradientBrush backgroundGradient = new LinearGradientBrush(
bounds,
Color.FromArgb(240, _glassColor),
Color.FromArgb(180, _glassColor),
LinearGradientMode.Vertical))
{
ColorBlend blend = new ColorBlend(3);
blend.Colors = new Color[] {
Color.FromArgb(240, _glassColor),
Color.FromArgb(210, _glassColor),
Color.FromArgb(180, _glassColor)
};
blend.Positions = new float[] { 0f, 0.5f, 1f };
backgroundGradient.InterpolationColors = blend;
g.FillPath(backgroundGradient, path);
}
// 上部光泽效果
Rectangle upperGloss = new Rectangle(
bounds.X,
bounds.Y,
bounds.Width,
bounds.Height / 2);
using (GraphicsPath glossPath = CreateRoundedRectangle(upperGloss,
_enableRoundedCorners ? _cornerRadius : 0))
using (LinearGradientBrush glossGradient = new LinearGradientBrush(
upperGloss,
Color.FromArgb(180, Color.White),
Color.FromArgb(0, Color.White),
LinearGradientMode.Vertical))
{
g.FillPath(glossGradient, glossPath);
}
// 顶部反光效果
int reflectionHeight = bounds.Height / 15;
Rectangle reflectionRect = new Rectangle(
bounds.X + bounds.Width / 4,
bounds.Y + 5,
bounds.Width / 2,
reflectionHeight);
using (LinearGradientBrush reflectionBrush = new LinearGradientBrush(
reflectionRect,
Color.FromArgb(150, Color.White),
Color.FromArgb(0, Color.White),
LinearGradientMode.Vertical))
{
if (_enableRoundedCorners)
{
using (GraphicsPath reflectionPath = CreateRoundedRectangle(reflectionRect,
reflectionHeight / 2))
{
g.FillPath(reflectionBrush, reflectionPath);
}
}
else
{
g.FillRectangle(reflectionBrush, reflectionRect);
}
}
}
// 绘制刻度
private void DrawScale(Graphics g, Rectangle scaleRect)
{
float stepValue = (_maximum - _minimum) / (_majorTickCount - 1);
float stepPixels = scaleRect.Height / (_majorTickCount - 1);
using (Pen scalePen = new Pen(_scaleColor, 1))
using (Font font = new Font("Segoe UI", 8, FontStyle.Regular))
using (StringFormat sf = new StringFormat())
{
sf.Alignment = StringAlignment.Far;
sf.LineAlignment = StringAlignment.Center;
// 绘制主刻度线和刻度值
for (int i = 0; i < _majorTickCount; i++)
{
float y = scaleRect.Bottom - (i * stepPixels);
float value = _minimum + (i * stepValue);
// 绘制主刻度线
using (Pen majorTickPen = new Pen(Color.FromArgb(180, _scaleColor), 1.5f))
{
g.DrawLine(majorTickPen,
scaleRect.Left - 15, y,
scaleRect.Right, y);
}
// 绘制刻度值
using (SolidBrush textBrush = new SolidBrush(_scaleColor))
{
g.DrawString(
value.ToString("0"),
font,
textBrush,
new RectangleF(0, y - 10, scaleRect.Left - 5, 20),
sf);
}
// 绘制小刻度线
if (i < _majorTickCount - 1)
{
float minorStep = stepPixels / (_minorTickCount + 1);
using (Pen minorTickPen = new Pen(Color.FromArgb(120, _scaleColor), 1))
{
for (int j = 1; j <= _minorTickCount; j++)
{
float minorY = y - (j * minorStep);
g.DrawLine(minorTickPen,
scaleRect.Left - 5, minorY,
scaleRect.Right, minorY);
}
}
}
}
}
}
// 绘制值指示器
private void DrawValueIndicator(Graphics g, Rectangle scaleRect)
{
float valuePosition = scaleRect.Bottom -
((currentAnimatedValue - Minimum) / (Maximum - Minimum) * scaleRect.Height);
// 创建指示器渐变
using (LinearGradientBrush indicatorBrush = new LinearGradientBrush(
new Point(scaleRect.Left - 15, (int)valuePosition - 2),
new Point(scaleRect.Right + 5, (int)valuePosition + 2),
Color.FromArgb(220, _valueColor),
Color.FromArgb(180, _valueColor)))
{
// 绘制带渐变的指示器
g.FillRectangle(indicatorBrush,
scaleRect.Left - 15,
valuePosition - 1.5f,
scaleRect.Right - scaleRect.Left + 20,
3);
// 添加高光效果
using (Pen highlightPen = new Pen(Color.FromArgb(80, Color.White), 1))
{
g.DrawLine(highlightPen,
scaleRect.Left - 15,
valuePosition - 1,
scaleRect.Right + 5,
valuePosition - 1);
}
}
}
// 释放资源
protected override void Dispose(bool disposing)
{
if (disposing)
{
animationTimer?.Dispose();
}
base.Dispose(disposing);
}
}
}
C#public partial class Form1 : Form
{
private LinearGauge linearGauge;
public Form1()
{
InitializeComponent();
// 创建并配置控件
linearGauge = new LinearGauge
{
Location = new Point(50, 50),
Minimum = 0,
Maximum = 100,
Value = 75
};
// 添加到窗体
this.Controls.Add(linearGauge);
}
}
控件提供了三个主要属性:
Minimum
:最小值Maximum
:最大值Value
:当前值使用红色线条标示当前值的位置。
这个自定义线性表控件实现了专业的刻度显示和值指示功能,可以方便地集成到Windows Forms应用程序中。通过调整属性和样式,可以满足不同的显示需求。代码结构清晰,易于维护和扩展。
希望这个实现对您有所帮助!如果需要更多功能,可以基于此代码进行扩展开发。
本文作者:rick
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!