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

目录

引言
控件特性概览
核心实现
1. 基础属性设计
2. 透明背景支持
3. 高质量绘图实现
4. 完整代码实现
使用示例
结论

引言

在工业自动化领域,阀门作为最基础的控制元件之一,其可视化显示对于操作员监控系统运行状态至关重要。本文将详细介绍如何使用C#和GDI+技术开发一个功能强大、外观精美的阀门控件,该控件不仅性能优异,还支持高DPI显示和各种自定义选项。

控件特性概览

  • 支持圆形和矩形(圆角可调)两种阀门形状
  • 垂直和水平两种显示方向
  • 可自定义开关状态颜色和文本
  • 支持管道显示控制
  • 高DPI显示支持
  • 透明背景支持
  • 事件驱动的状态变更机制

核心实现

1. 基础属性设计

控件的核心是一系列可自定义的属性,包括:

C#
private bool _isOpen = false; // 阀门开关状态 private string _valveText = "阀门"; // 阀门显示文本 private Color _openColor = Color.Green; // 开启颜色 private Color _closeColor = Color.Red; // 关闭颜色 private ValveOrientation _orientation = ValveOrientation.Vertical; // 显示方向 private float _pipeWidth = 2f; // 管道宽度 private Color _pipeColor = Color.Black; // 管道颜色

2. 透明背景支持

为实现真正的透明效果,需要特别设置控件样式和重写相关方法:

C#
public ValveControl() { SetStyle(ControlStyles.SupportsTransparentBackColor, true); SetStyle(ControlStyles.OptimizedDoubleBuffer, false); SetStyle(ControlStyles.AllPaintingInWmPaint, true); SetStyle(ControlStyles.UserPaint, true); SetStyle(ControlStyles.ResizeRedraw, true); SetStyle(ControlStyles.Opaque, false); this.BackColor = Color.Transparent; } protected override CreateParams CreateParams { get { CreateParams cp = base.CreateParams; cp.ExStyle |= 0x20; // WS_EX_TRANSPARENT return cp; } }

3. 高质量绘图实现

为确保控件在各种分辨率下都能完美显示,实现了高质量绘图模式:

C#
protected override void OnPaint(PaintEventArgs e) { // 设置高质量绘图模式 e.Graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic; e.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality; e.Graphics.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighQuality; e.Graphics.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality; // 使用双倍分辨率缓冲绘图 using (var bmp = new Bitmap(this.Width * 2, this.Height * 2)) { // ... 绘制代码 ... } }

4. 完整代码实现

C#
using System; using System.ComponentModel; using System.Drawing; using System.Drawing.Design; using System.Windows.Forms; using System.Windows.Forms.Design; namespace AppControls { // 形状枚举 public enum ValveShape { Circle, Rectangle } public class ValveControl : Control { private bool _isOpen = false; private string _valveText = "阀门"; private Color _openColor = Color.Green; private Color _closeColor = Color.Red; private ValveOrientation _orientation = ValveOrientation.Vertical; private float _pipeWidth = 2f; private Color _pipeColor = Color.Black; private bool _showFirstPipe = true; // 第一根管道(上/左)显示控制 private bool _showSecondPipe = true; // 第二根管道(下/右)显示控制 private bool _showStatus = true; // 状态显示控制 private string _openText = "开启"; // 开启状态文本 private string _closeText = "关闭"; // 关闭状态文本 // 形状字段 private ValveShape _valveShape = ValveShape.Circle; private int _cornerRadius = 10; // 事件声明 public event EventHandler ValveOpened; public event EventHandler ValveClosed; // 定义阀门方向的枚举 public enum ValveOrientation { Horizontal, Vertical } // 第一根管道显示控制属性(上管道/左管道) [Description("显示第一根管道(垂直方向时为上管道,水平方向时为左管道)")] [DefaultValue(true)] public bool ShowFirstPipe { get { return _showFirstPipe; } set { _showFirstPipe = value; Invalidate(); } } // 第二根管道显示控制属性(下管道/右管道) [Description("显示第二根管道(垂直方向时为下管道,水平方向时为右管道)")] [DefaultValue(true)] public bool ShowSecondPipe { get { return _showSecondPipe; } set { _showSecondPipe = value; Invalidate(); } } // 管道宽度属性 [Description("管道宽度")] [DefaultValue(2f)] public float PipeWidth { get { return _pipeWidth; } set { if (value > 0) { _pipeWidth = value; Invalidate(); } } } // 管道颜色属性 [Description("管道颜色")] public Color PipeColor { get { return _pipeColor; } set { _pipeColor = value; Invalidate(); } } [Description("阀门显示方向")] public ValveOrientation Orientation { get { return _orientation; } set { _orientation = value; if (value == ValveOrientation.Vertical) { this.Size = new Size(100, 150); } else { this.Size = new Size(150, 100); } Invalidate(); } } [Description("阀门状态")] public bool IsOpen { get { return _isOpen; } set { if (_isOpen != value) // 只在状态确实发生改变时才触发事件 { _isOpen = value; // 触发相应的事件 if (_isOpen) { OnValveOpened(EventArgs.Empty); } else { OnValveClosed(EventArgs.Empty); } Invalidate(); } } } [Description("阀门显示文本")] public string ValveText { get { return _valveText; } set { _valveText = value; Invalidate(); } } [Description("阀门打开时的颜色")] public Color OpenColor { get { return _openColor; } set { _openColor = value; Invalidate(); } } [Description("阀门关闭时的颜色")] public Color CloseColor { get { return _closeColor; } set { _closeColor = value; Invalidate(); } } [Description("是否显示阀门状态文本")] [DefaultValue(true)] public bool ShowStatus { get { return _showStatus; } set { _showStatus = value; Invalidate(); } } [Description("阀门开启时显示的文本")] [DefaultValue("开启")] public string OpenText { get { return _openText; } set { _openText = value; if (_isOpen) Invalidate(); } } [Description("阀门关闭时显示的文本")] [DefaultValue("关闭")] public string CloseText { get { return _closeText; } set { _closeText = value; if (!_isOpen) Invalidate(); } } // 形状属性 [Description("阀门形状")] [DefaultValue(ValveShape.Circle)] public ValveShape ValveShape { get { return _valveShape; } set { _valveShape = value; Invalidate(); } } [Description("矩形模式下的圆角半径")] [DefaultValue(10)] public int CornerRadius { get { return _cornerRadius; } set { if (value >= 0) { _cornerRadius = value; Invalidate(); } } } public ValveControl() { // 设置关键的控件样式 SetStyle(ControlStyles.SupportsTransparentBackColor, true); SetStyle(ControlStyles.OptimizedDoubleBuffer, false);//这个影响透明 SetStyle(ControlStyles.AllPaintingInWmPaint, true); SetStyle(ControlStyles.UserPaint, true); SetStyle(ControlStyles.ResizeRedraw, true); // 启用透明 SetStyle(ControlStyles.Opaque, false); this.Size = new Size(100, 150); this.BackColor = Color.Transparent; this.Click += new EventHandler(ValveControl_Click); } // 重写这个方法来支持真正的透明 protected override CreateParams CreateParams { get { CreateParams cp = base.CreateParams; cp.ExStyle |= 0x20; // WS_EX_TRANSPARENT return cp; } } protected override void OnPaintBackground(PaintEventArgs e) { // 不执行任何操作,保持背景透明 } private void ValveControl_Click(object sender, EventArgs e) { IsOpen = !IsOpen; } protected override void OnPaint(PaintEventArgs e) { // 设置高质量绘图模式 e.Graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic; e.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality; e.Graphics.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighQuality; e.Graphics.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality; // 使用高分辨率绘图 using (var bmp = new Bitmap(this.Width * 2, this.Height * 2)) { using (var tempG = Graphics.FromImage(bmp)) { // 设置高质量绘图模式 tempG.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic; tempG.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality; tempG.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighQuality; tempG.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality; tempG.ScaleTransform(2, 2); // 缩放以支持高DPI // 计算绘制区域 Rectangle valveRect; if (_orientation == ValveOrientation.Vertical) { valveRect = new Rectangle( Width / 4, Height / 4, Width / 2, Height / 2 ); } else { valveRect = new Rectangle( Width / 4, Height / 4, Width / 2, Height / 2 ); } // 绘制管道 using (Pen pipePen = new Pen(_pipeColor, _pipeWidth)) { pipePen.EndCap = System.Drawing.Drawing2D.LineCap.Round; pipePen.StartCap = System.Drawing.Drawing2D.LineCap.Round; pipePen.LineJoin = System.Drawing.Drawing2D.LineJoin.Round; if (_orientation == ValveOrientation.Vertical) { if (_showFirstPipe) { tempG.DrawLine(pipePen, new Point(Width / 2, 0), new Point(Width / 2, valveRect.Top)); } if (_showSecondPipe) { tempG.DrawLine(pipePen, new Point(Width / 2, valveRect.Bottom), new Point(Width / 2, Height)); } } else { if (_showFirstPipe) { tempG.DrawLine(pipePen, new Point(0, Height / 2), new Point(valveRect.Left, Height / 2)); } if (_showSecondPipe) { tempG.DrawLine(pipePen, new Point(valveRect.Right, Height / 2), new Point(Width, Height / 2)); } } } // 绘制阀门主体 using (SolidBrush valveBrush = new SolidBrush(_isOpen ? _openColor : _closeColor)) using (Pen valvePen = new Pen(Color.Black, 2)) { valvePen.EndCap = System.Drawing.Drawing2D.LineCap.Round; valvePen.StartCap = System.Drawing.Drawing2D.LineCap.Round; valvePen.LineJoin = System.Drawing.Drawing2D.LineJoin.Round; if (_valveShape == ValveShape.Circle) { // 圆形阀门 tempG.FillEllipse(valveBrush, valveRect); tempG.DrawEllipse(valvePen, valveRect); } else { // 矩形阀门(圆角) using (var path = new System.Drawing.Drawing2D.GraphicsPath()) { path.AddArc(valveRect.X, valveRect.Y, _cornerRadius * 2, _cornerRadius * 2, 180, 90); path.AddArc(valveRect.Right - _cornerRadius * 2, valveRect.Y, _cornerRadius * 2, _cornerRadius * 2, 270, 90); path.AddArc(valveRect.Right - _cornerRadius * 2, valveRect.Bottom - _cornerRadius * 2, _cornerRadius * 2, _cornerRadius * 2, 0, 90); path.AddArc(valveRect.X, valveRect.Bottom - _cornerRadius * 2, _cornerRadius * 2, _cornerRadius * 2, 90, 90); path.CloseFigure(); tempG.FillPath(valveBrush, path); tempG.DrawPath(valvePen, path); } } } // 绘制文本 using (var textFont = new Font(this.Font.FontFamily, this.Font.Size, FontStyle.Regular, GraphicsUnit.Point)) using (var sf = new StringFormat()) { sf.Alignment = StringAlignment.Center; sf.LineAlignment = StringAlignment.Center; sf.FormatFlags |= StringFormatFlags.NoWrap; using (var textBrush = new SolidBrush(Color.Black)) { // 使用TextRenderingHint.ClearTypeGridFit提高文本清晰度 tempG.TextRenderingHint = System.Drawing.Text.TextRenderingHint.ClearTypeGridFit; tempG.DrawString(_valveText, textFont, textBrush, valveRect, sf); } } // 绘制状态文本 if (_showStatus) { string status = _isOpen ? _openText : _closeText; using (var smallFont = new Font(this.Font.FontFamily, 8, FontStyle.Regular, GraphicsUnit.Point)) using (var sf = new StringFormat()) { sf.Alignment = StringAlignment.Center; Rectangle statusRect; if (_orientation == ValveOrientation.Vertical) { statusRect = new Rectangle( 0, valveRect.Bottom + 5, Width, 20 ); } else { statusRect = new Rectangle( valveRect.Right + 5, 0, (Width - valveRect.Right - 5), Height ); } tempG.TextRenderingHint = System.Drawing.Text.TextRenderingHint.ClearTypeGridFit; tempG.DrawString(status, smallFont, Brushes.Black, statusRect, sf); } } // 将高分辨率图像绘制到控件上 e.Graphics.DrawImage(bmp, 0, 0, this.Width, this.Height); } } } // 事件触发方法 protected virtual void OnValveOpened(EventArgs e) { ValveOpened?.Invoke(this, e); } protected virtual void OnValveClosed(EventArgs e) { ValveClosed?.Invoke(this, e); } } }

使用示例

image.png

结论

本文介绍的阀门控件实现了工控系统中常用的阀门可视化需求,具有良好的可定制性和性能表现。通过合理的代码架构和绘图优化,确保了控件在实际应用中的稳定性和效率。开发者可以基于此继续扩展,添加更多功能特性,以满足不同场景的需求。

这个控件的实现充分展示了C# GDI+在工控图形开发中的应用价值,为工控软件开发提供了一个很好的参考示例。

本文作者:rick

本文链接:

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