在工业自动化领域,阀门作为最基础的控制元件之一,其可视化显示对于操作员监控系统运行状态至关重要。本文将详细介绍如何使用C#和GDI+技术开发一个功能强大、外观精美的阀门控件,该控件不仅性能优异,还支持高DPI显示和各种自定义选项。
控件的核心是一系列可自定义的属性,包括:
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; // 管道颜色
为实现真正的透明效果,需要特别设置控件样式和重写相关方法:
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;
}
}
为确保控件在各种分辨率下都能完美显示,实现了高质量绘图模式:
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))
{
// ... 绘制代码 ...
}
}
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);
}
}
}
本文介绍的阀门控件实现了工控系统中常用的阀门可视化需求,具有良好的可定制性和性能表现。通过合理的代码架构和绘图优化,确保了控件在实际应用中的稳定性和效率。开发者可以基于此继续扩展,添加更多功能特性,以满足不同场景的需求。
这个控件的实现充分展示了C# GDI+在工控图形开发中的应用价值,为工控软件开发提供了一个很好的参考示例。
本文作者:rick
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!