在工业控制、监控系统等应用中,指示灯是一个常用的可视化控件。本文将详细介绍如何使用C#和GDI+开发一个功能丰富的指示灯自定义控件。这个控件将支持多种颜色、闪烁效果、3D效果等特性。
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 IndicatorLight : Control
{
#region 私有字段
private bool isOn = false;
private Color onColor = Color.Lime;
private Color offColor = Color.DarkGray;
private bool is3D = true;
private bool isBlinking = false;
private int blinkInterval = 500;
private Timer blinkTimer;
private bool isCircular = true;
private int borderWidth = 1;
private Color borderColor = Color.DarkGray;
private bool temporaryState = false;
#endregion
#region 动画相关字段
private bool isBreathing = false;
private bool isRotating = false;
private bool isRippling = false;
private float breathingOpacity = 1.0f;
private float rotationAngle = 0f;
private List<float> rippleRadii = new List<float>();
private const float BREATHING_STEP = 0.05f;
private const float ROTATION_STEP = 2f;
private const float RIPPLE_STEP = 2f;
private const int MAX_RIPPLES = 3;
private Timer animationTimer;
private bool breathingDirection = false; // false为淡出,true为淡入
#endregion
#region 属性
[Category("Animation")]
public bool IsBreathing
{
get { return isBreathing; }
set
{
isBreathing = value;
if (isBreathing)
animationTimer.Start();
else if (!IsAnimating())
animationTimer.Stop();
Invalidate();
}
}
[Category("Animation")]
public bool IsRotating
{
get { return isRotating; }
set
{
isRotating = value;
if (isRotating)
animationTimer.Start();
else if (!IsAnimating())
animationTimer.Stop();
Invalidate();
}
}
[Category("Animation")]
public bool IsRippling
{
get { return isRippling; }
set
{
isRippling = value;
if (isRippling)
animationTimer.Start();
else
{
rippleRadii.Clear();
if (!IsAnimating())
animationTimer.Stop();
}
Invalidate();
}
}
[Category("Appearance")]
public bool IsOn
{
get { return isOn; }
set
{
isOn = value;
Invalidate();
}
}
[Category("Appearance")]
public Color OnColor
{
get { return onColor; }
set
{
onColor = value;
Invalidate();
}
}
[Category("Appearance")]
public Color OffColor
{
get { return offColor; }
set
{
offColor = value;
Invalidate();
}
}
[Category("Appearance")]
public bool Is3D
{
get { return is3D; }
set
{
is3D = value;
Invalidate();
}
}
[Category("Behavior")]
public bool IsBlinking
{
get { return isBlinking; }
set
{
isBlinking = value;
if (isBlinking)
blinkTimer.Start();
else
{
blinkTimer.Stop();
temporaryState = false;
}
Invalidate();
}
}
[Category("Behavior")]
public int BlinkInterval
{
get { return blinkInterval; }
set
{
blinkInterval = value;
blinkTimer.Interval = value;
}
}
[Category("Appearance")]
public bool IsCircular
{
get { return isCircular; }
set
{
isCircular = value;
Invalidate();
}
}
[Category("Appearance")]
public int BorderWidth
{
get { return borderWidth; }
set
{
borderWidth = value;
Invalidate();
}
}
[Category("Appearance")]
public Color BorderColor
{
get { return borderColor; }
set
{
borderColor = value;
Invalidate();
}
}
#endregion
#region 构造函数
public IndicatorLight()
{
SetStyle(ControlStyles.SupportsTransparentBackColor, true);
SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
SetStyle(ControlStyles.AllPaintingInWmPaint, true);
SetStyle(ControlStyles.UserPaint, true);
SetStyle(ControlStyles.ResizeRedraw, true);
Size = new Size(30, 30);
BackColor = Color.Transparent;
blinkTimer = new Timer();
blinkTimer.Interval = blinkInterval;
blinkTimer.Tick += BlinkTimer_Tick;
animationTimer = new Timer();
animationTimer.Interval = 50; // 20fps
animationTimer.Tick += AnimationTimer_Tick;
}
#endregion
private void AnimationTimer_Tick(object sender, EventArgs e)
{
bool needInvalidate = false;
// 处理呼吸效果
if (isBreathing)
{
if (!breathingDirection)
{
breathingOpacity -= BREATHING_STEP;
if (breathingOpacity <= 0.3f)
{
breathingOpacity = 0.3f;
breathingDirection = true;
}
}
else
{
breathingOpacity += BREATHING_STEP;
if (breathingOpacity >= 1.0f)
{
breathingOpacity = 1.0f;
breathingDirection = false;
}
}
needInvalidate = true;
}
// 处理旋转效果
if (isRotating)
{
rotationAngle = (rotationAngle + ROTATION_STEP) % 360;
needInvalidate = true;
}
// 处理波纹效果
if (isRippling)
{
// 更新现有波纹
for (int i = rippleRadii.Count - 1; i >= 0; i--)
{
rippleRadii[i] += RIPPLE_STEP;
if (rippleRadii[i] > Math.Max(Width, Height))
{
rippleRadii.RemoveAt(i);
}
}
// 添加新波纹
if (rippleRadii.Count < MAX_RIPPLES && (rippleRadii.Count == 0 || rippleRadii[rippleRadii.Count - 1] > 20))
{
rippleRadii.Add(0f);
}
needInvalidate = true;
}
if (needInvalidate)
Invalidate();
}
private bool IsAnimating()
{
return isBreathing || isRotating || isRippling || isBlinking;
}
#region 事件处理
private void BlinkTimer_Tick(object sender, EventArgs e)
{
temporaryState = !temporaryState;
Invalidate();
}
#endregion
#region 绘制方法
protected override void OnPaint(PaintEventArgs e)
{
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
bool currentState = isBlinking ? temporaryState : isOn;
Color currentColor = currentState ? onColor : offColor;
// 应用呼吸效果的透明度
if (isBreathing)
{
currentColor = Color.FromArgb(
(int)(255 * breathingOpacity),
currentColor.R,
currentColor.G,
currentColor.B
);
}
// 保存当前图形状态
GraphicsState state = e.Graphics.Save();
// 应用旋转
if (isRotating)
{
e.Graphics.TranslateTransform(Width / 2f, Height / 2f);
e.Graphics.RotateTransform(rotationAngle);
e.Graphics.TranslateTransform(-Width / 2f, -Height / 2f);
}
using (GraphicsPath path = new GraphicsPath())
{
Rectangle bounds = new Rectangle(
borderWidth,
borderWidth,
Width - 2 * borderWidth - 1,
Height - 2 * borderWidth - 1
);
if (isCircular)
path.AddEllipse(bounds);
else
path.AddRectangle(bounds);
// 绘制主体
if (is3D)
{
// 3D效果绘制代码保持不变
using (PathGradientBrush gradientBrush = new PathGradientBrush(path))
{
gradientBrush.CenterColor = LightenColor(currentColor, 50);
gradientBrush.SurroundColors = new Color[] { currentColor };
gradientBrush.FocusScales = new PointF(0.8f, 0.8f);
e.Graphics.FillPath(gradientBrush, path);
}
// 高光效果
DrawHighlight(e.Graphics, bounds);
}
else
{
using (SolidBrush brush = new SolidBrush(currentColor))
{
e.Graphics.FillPath(brush, path);
}
}
// 绘制边框
if (borderWidth > 0)
{
using (Pen borderPen = new Pen(borderColor, borderWidth))
{
e.Graphics.DrawPath(borderPen, path);
}
}
}
// 恢复图形状态
e.Graphics.Restore(state);
// 绘制波纹效果
if (isRippling && rippleRadii.Count > 0)
{
DrawRipples(e.Graphics);
}
base.OnPaint(e);
}
#endregion
private void DrawHighlight(Graphics g, Rectangle bounds)
{
using (GraphicsPath highlightPath = new GraphicsPath())
{
// 计算高光区域(左上角域)
Rectangle highlightBounds = new Rectangle(
bounds.X + bounds.Width / 4,
bounds.Y + bounds.Height / 4,
bounds.Width / 4,
bounds.Height / 4
);
highlightPath.AddEllipse(highlightBounds);
// 创建径向渐变画刷实现高光效果
using (PathGradientBrush highlightBrush = new PathGradientBrush(highlightPath))
{
highlightBrush.CenterColor = Color.FromArgb(180, Color.White);
highlightBrush.SurroundColors = new Color[] { Color.FromArgb(0, Color.White) };
g.FillPath(highlightBrush, highlightPath);
}
}
}
private void DrawRipples(Graphics g)
{
float centerX = Width / 2f;
float centerY = Height / 2f;
foreach (float radius in rippleRadii)
{
// 计算波纹透明度
float alpha = 1.0f - (radius / Math.Max(Width, Height));
if (alpha <= 0) continue;
// 绘制波纹圆圈
using (Pen ripplePen = new Pen(Color.FromArgb((int)(alpha * 255), onColor), 2))
{
g.DrawEllipse(ripplePen,
centerX - radius,
centerY - radius,
radius * 2,
radius * 2);
}
}
}
#region 辅助方法
private Color LightenColor(Color color, int amount)
{
return Color.FromArgb(
color.A,
Math.Min(color.R + amount, 255),
Math.Min(color.G + amount, 255),
Math.Min(color.B + amount, 255)
);
}
#endregion
#region 公共方法
public void StartBlinking()
{
IsBlinking = true;
}
public void StopBlinking()
{
IsBlinking = false;
}
public void Toggle()
{
IsOn = !IsOn;
}
public void StartBreathing()
{
IsBreathing = true;
}
public void StopBreathing()
{
IsBreathing = false;
}
public void StartRotating()
{
IsRotating = true;
}
public void StopRotating()
{
IsRotating = false;
}
public void StartRippling()
{
IsRippling = true;
}
public void StopRippling()
{
IsRippling = false;
}
#endregion
#region 析构函数
protected override void Dispose(bool disposing)
{
if (disposing)
{
blinkTimer?.Dispose();
animationTimer?.Dispose();
}
base.Dispose(disposing);
}
#endregion
}
}
这个指示灯控件实现了丰富的功能,可以满足大多数工业控制和监控系统的需求。通过属性设置,可以轻松地自定义外观和行为。代码结构清晰,易于维护和扩展。
希望这个教程能帮助你理解如何使用C#和GDI+开发专业的自定义控件。如果你有任何问题或建议,欢迎讨论和交流。
本文作者:rick
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!