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

目录

简介
控件特性
完整代码实现
总结

简介

在工业控制、监控系统等应用中,指示灯是一个常用的可视化控件。本文将详细介绍如何使用C#和GDI+开发一个功能丰富的指示灯自定义控件。这个控件将支持多种颜色、闪烁效果、3D效果等特性。

控件特性

  • 支持多种颜色设置
  • 可配置的闪烁效果
  • 3D立体效果
  • 可自定义大小
  • 支持不同的形状(圆形、方形)
  • 鼠标悬停效果
  • 支持禁用状态显示
  • 可配置的边框样式

完整代码实现

image.png

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 许可协议。转载请注明出处!