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

目录

简介
实现原理
完整代码实现
注意事项
扩展建议
总结

简介

本文将详细介绍如何使用C#和GDI+技术实现一个精美的液晶数字显示控件。这个控件支持显示数字0-9和小数点,可以用来模拟各种电子设备上的LCD显示效果。

实现原理

液晶数字显示是由7段LED组成的,每个数字由不同的段组合显示。我们将使用GDI+的绘图功能来实现发光效果,使用Path来绘制每个段,并添加渐变效果使其看起来更真实。

完整代码实现

image.png

C#
using System; using System.Collections.Generic; using System.Drawing.Drawing2D; using System.Linq; using System.Text; using System.Threading.Tasks; using Timer = System.Windows.Forms.Timer; namespace AppControls { public class LcdDisplayControl : Control { private string _displayText = "0"; private Color _digitColor = Color.LightGreen; private Color _backgroundColor = Color.Black; private const float SEGMENT_WIDTH_RATIO = 0.15f; //每个发光段的宽度比例 private const float DIGIT_HEIGHT_RATIO = 0.8f; //数字显示区域的高度比例 private const float SEGMENT_GAP_RATIO = 0.05f; //段之间的间隙比例 private float _padding = 2f; private Color _shadowColor = Color.FromArgb(30, Color.LightGreen); // 默认投影颜色 private float _shadowOffset = 1.5f; // 默认投影偏移量 private bool _enableGlassEffect = true; private Color _glassHighlightColor = Color.FromArgb(40, Color.White); private float _glassEffectHeight = 0.4f; // 玻璃效果占控件高度的比例 private Timer _animationTimer; private double _currentValue = 0; private double _targetValue = 0; private bool _isAnimating = false; private int _animationDuration = 1000; // 默认动画持续时间(毫秒) private DateTime _animationStartTime; private string _originalFormat = "0"; // 保存显示格式 public float Padding { get => _padding; set { if (_padding != value) { _padding = Math.Max(0, value); Invalidate(); } } } public int AnimationDuration { get => _animationDuration; set { if (_animationDuration != value && value > 0) { _animationDuration = value; } } } public bool EnableGlassEffect { get => _enableGlassEffect; set { if (_enableGlassEffect != value) { _enableGlassEffect = value; Invalidate(); } } } public Color GlassHighlightColor { get => _glassHighlightColor; set { if (_glassHighlightColor != value) { _glassHighlightColor = value; Invalidate(); } } } public Color ShadowColor { get => _shadowColor; set { if (_shadowColor != value) { _shadowColor = value; Invalidate(); } } } public float ShadowOffset { get => _shadowOffset; set { if (_shadowOffset != value) { _shadowOffset = Math.Max(0, value); // 确保偏移量不为负数 Invalidate(); } } } public LcdDisplayControl() { SetStyle(ControlStyles.DoubleBuffer | ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint | ControlStyles.ResizeRedraw, true); ForeColor = _digitColor; EnableGlassEffect = true; // 默认启用玻璃效果 _animationTimer = new Timer(); _animationTimer.Interval = 16; // 约60fps _animationTimer.Tick += AnimationTimer_Tick; } public string DisplayText { get => _displayText; set { if (_displayText != value) { // 尝试解析新值 if (double.TryParse(value, out double newValue)) { // 保存显示格式 _originalFormat = value.Contains(".") ? "F" + (value.Length - value.IndexOf('.') - 1) : "0"; // 开始动画 StartAnimation(newValue); } else { // 如果不是数字,直接设置 _displayText = value; Invalidate(); } } } } public Color DigitColor { get => _digitColor; set { if (_digitColor != value) { _digitColor = value; Invalidate(); } } } private void StartAnimation(double targetValue) { _targetValue = targetValue; _currentValue = double.TryParse(_displayText, out double currentValue) ? currentValue : 0; if (_currentValue == _targetValue) return; _animationStartTime = DateTime.Now; _isAnimating = true; _animationTimer.Start(); } private void AnimationTimer_Tick(object sender, EventArgs e) { var elapsed = (DateTime.Now - _animationStartTime).TotalMilliseconds; var progress = Math.Min(elapsed / _animationDuration, 1.0); // 使用缓动函数使动画更自然 progress = EaseOutCubic(progress); // 计算当前值 _currentValue = _currentValue + (_targetValue - _currentValue) * progress; // 更新显示 _displayText = _currentValue.ToString(_originalFormat); Invalidate(); // 检查动画是否完成 if (progress >= 1.0) { _animationTimer.Stop(); _isAnimating = false; _currentValue = _targetValue; _displayText = _targetValue.ToString(_originalFormat); Invalidate(); } } // 缓动函数 private double EaseOutCubic(double t) { return 1 - Math.Pow(1 - t, 3); } protected override void OnPaint(PaintEventArgs e) { base.OnPaint(e); Graphics g = e.Graphics; g.SmoothingMode = SmoothingMode.HighQuality; g.InterpolationMode = InterpolationMode.HighQualityBicubic; g.PixelOffsetMode = PixelOffsetMode.HighQuality; g.CompositingQuality = CompositingQuality.HighQuality; // 绘制背景和边框 using (var bgBrush = new SolidBrush(_backgroundColor)) { g.FillRectangle(bgBrush, ClientRectangle); } // 计算实际显示区域(考虑内边距和边框) float effectivePadding = _padding; float displayAreaWidth = Width - (effectivePadding * 2); float displayAreaHeight = Height - (effectivePadding * 2); // 计算单个数字的大小 float digitWidth = displayAreaWidth / _displayText.Length; float digitHeight = displayAreaHeight * 0.8f; // 起始位置(考虑内边距和边框) float x = effectivePadding; float y = effectivePadding + (displayAreaHeight - digitHeight) / 2; // 绘制数字 for (int i = 0; i < _displayText.Length; i++) { if (_displayText[i] == '.') { DrawDecimalPoint(g, x, y, digitWidth, digitHeight); x += digitWidth * 0.3f; } else { DrawDigit(g, _displayText[i], x, y, digitWidth, digitHeight); x += digitWidth; } } // 如果启用玻璃效果,绘制玻璃效果 if (_enableGlassEffect) { DrawGlassEffect(g); } } // 玻璃效果绘制方法 private void DrawGlassEffect(Graphics g) { float glassHeight = Height * _glassEffectHeight; // 创建渐变画笷 using (var path = new GraphicsPath()) { path.AddRectangle(new RectangleF(0, 0, Width, glassHeight)); // 创建渐变 using (var brush = new LinearGradientBrush( new PointF(0, 0), new PointF(0, glassHeight), Color.FromArgb(60, _glassHighlightColor), Color.FromArgb(10, _glassHighlightColor))) { g.FillPath(brush, path); } // 添加微弱的边缘高光 float highlightThickness = 1.0f; using (var highlightBrush = new LinearGradientBrush( new RectangleF(0, 0, Width, highlightThickness), Color.FromArgb(100, _glassHighlightColor), Color.FromArgb(0, _glassHighlightColor), LinearGradientMode.Vertical)) { g.FillRectangle(highlightBrush, 0, 0, Width, highlightThickness); } } } private void DrawDigit(Graphics g, char digit, float x, float y, float width, float height) { bool[] segments = GetSegments(digit); float segmentWidth = width * SEGMENT_WIDTH_RATIO; float segmentLength = width * 0.8f; float gap = width * SEGMENT_GAP_RATIO; // 水平段 if (segments[0]) DrawHorizontalSegment(g, x + gap, y, segmentLength, segmentWidth); // 顶段 if (segments[3]) DrawHorizontalSegment(g, x + gap, y + height / 2, segmentLength, segmentWidth); // 中段 if (segments[6]) DrawHorizontalSegment(g, x + gap, y + height - segmentWidth, segmentLength, segmentWidth); // 底段 // 垂直段 if (segments[1]) DrawVerticalSegment(g, x, y + gap, segmentWidth, height / 2 - gap); // 左上 if (segments[2]) DrawVerticalSegment(g, x + segmentLength, y + gap, segmentWidth, height / 2 - gap); // 右上 if (segments[4]) DrawVerticalSegment(g, x, y + height / 2 + gap, segmentWidth, height / 2 - gap); // 左下 if (segments[5]) DrawVerticalSegment(g, x + segmentLength, y + height / 2 + gap, segmentWidth, height / 2 - gap); // 右下 } private void DrawHorizontalSegment(Graphics g, float x, float y, float length, float width) { using (var path = new GraphicsPath()) { // 创建水平段的路径 path.AddLine(x + width / 2, y, x + length - width / 2, y); path.AddLine(x + length, y + width / 2, x + length - width / 2, y + width); path.AddLine(x + width / 2, y + width, x, y + width / 2); path.CloseFigure(); // 绘制阴影效果 using (var shadowBrush = new SolidBrush(_shadowColor)) { var shadowPath = (GraphicsPath)path.Clone(); var shadowMatrix = new Matrix(); shadowMatrix.Translate(_shadowOffset, _shadowOffset); shadowPath.Transform(shadowMatrix); g.FillPath(shadowBrush, shadowPath); shadowPath.Dispose(); } // 绘制主体 using (var brush = new SolidBrush(_digitColor)) { g.FillPath(brush, path); } // 如果启用玻璃效果,添加额外的光泽 if (_enableGlassEffect) { using (var glassBrush = new LinearGradientBrush( new RectangleF(x, y, length, width), Color.FromArgb(40, Color.White), Color.FromArgb(10, Color.White), LinearGradientMode.Vertical)) { g.FillPath(glassBrush, path); } } // 添加发光边缘 using (var pen = new Pen(Color.FromArgb(100, _digitColor), 0.5f)) { g.DrawPath(pen, path); } } } private void DrawVerticalSegment(Graphics g, float x, float y, float width, float length) { using (var path = new GraphicsPath()) { path.AddLine(x, y + width / 2, x + width / 2, y); path.AddLine(x + width, y + width / 2, x + width / 2, y + length); path.AddLine(x, y + length - width / 2, x, y + width / 2); path.CloseFigure(); // 绘制阴影 using (var shadowBrush = new SolidBrush(_shadowColor)) { var shadowPath = (GraphicsPath)path.Clone(); var shadowMatrix = new Matrix(); shadowMatrix.Translate(_shadowOffset, _shadowOffset); shadowPath.Transform(shadowMatrix); g.FillPath(shadowBrush, shadowPath); shadowPath.Dispose(); } // 绘制主体 using (var brush = new SolidBrush(_digitColor)) { g.FillPath(brush, path); } // 如果启用玻璃效果,添加额外的光泽 if (_enableGlassEffect) { using (var glassBrush = new LinearGradientBrush( new RectangleF(x, y, width, length), Color.FromArgb(40, Color.White), Color.FromArgb(10, Color.White), LinearGradientMode.Vertical)) { g.FillPath(glassBrush, path); } } // 添加发光边缘 using (var pen = new Pen(Color.FromArgb(100, _digitColor), 0.5f)) { g.DrawPath(pen, path); } } } private void DrawDecimalPoint(Graphics g, float x, float y, float width, float height) { float dotSize = width * 0.2f; // 绘制阴影效果 using (var shadowBrush = new SolidBrush(_shadowColor)) { g.FillEllipse(shadowBrush, x + _shadowOffset, y + height - dotSize + _shadowOffset, dotSize, dotSize); } // 绘制主体 using (var brush = new SolidBrush(_digitColor)) { g.FillEllipse(brush, x, y + height - dotSize, dotSize, dotSize); } // 添加发光边缘 using (var pen = new Pen(Color.FromArgb(100, _digitColor), 0.5f)) { g.DrawEllipse(pen, x, y + height - dotSize, dotSize, dotSize); } } private bool[] GetSegments(char digit) { // 7段显示的状态表 [顶, 左上, 右上, 中, 左下, 右下, 底] switch (digit) { case '0': return new bool[] { true, true, true, false, true, true, true }; case '1': return new bool[] { false, false, true, false, false, true, false }; case '2': return new bool[] { true, false, true, true, true, false, true }; case '3': return new bool[] { true, false, true, true, false, true, true }; case '4': return new bool[] { false, true, true, true, false, true, false }; case '5': return new bool[] { true, true, false, true, false, true, true }; case '6': return new bool[] { true, true, false, true, true, true, true }; case '7': return new bool[] { true, false, true, false, false, true, false }; case '8': return new bool[] { true, true, true, true, true, true, true }; case '9': return new bool[] { true, true, true, true, false, true, true }; default: return new bool[] { false, false, false, false, false, false, false }; } } protected override void Dispose(bool disposing) { if (disposing) { if (_animationTimer != null) { _animationTimer.Stop(); _animationTimer.Dispose(); } } base.Dispose(disposing); } } }

注意事项

  1. 控件的最小尺寸应该根据显示字符的数量来确定,
  2. 发光效果的渲染可能在某些低配置机器上造成性能影响,如有需要可以简化绘制方式。
  3. 控件的尺寸变化会自动触发重绘,确保显示效果始终保持最佳状态。

扩展建议

  1. 可以添加动画效果,实现数字切换时的过渡动画。
  2. 可以添加不同的显示样式,如不同的段形状或发光效果。
  3. 可以添加显示方向的支持,实现垂直方向的显示。
  4. 可以添加更多的自定义属性,如段的宽度比例、间距等。

总结

这个液晶数字显示控件实现了一个专业的显示效果,可以用于各种需要数字显示的场景。通过GDI+的高级绘图功能,实现了平滑的显示效果和漂亮的发光效果。代码结构清晰,易于扩展和维护。

本文作者:rick

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!