using System; using System.Collections.Generic; using System.ComponentModel; using System.Drawing; using System.Data; using System.Linq; using System.Text; using System.Windows.Forms; using System.Drawing.Drawing2D; namespace HslCommunication.Controls { /// /// 仪表盘控件类 /// public partial class UserGaugeChart : UserControl { /// /// 实例化一个仪表盘控件 /// public UserGaugeChart( ) { InitializeComponent( ); pen_gauge_border = new Pen( color_gauge_border ); brush_gauge_pointer = new SolidBrush( color_gauge_pointer ); centerFormat = new StringFormat( ); centerFormat.Alignment = StringAlignment.Center; centerFormat.LineAlignment = StringAlignment.Center; pen_gauge_alarm = new Pen( Color.OrangeRed, 3f ); pen_gauge_alarm.DashStyle = DashStyle.Custom; pen_gauge_alarm.DashPattern = new float[] { 5, 1 }; hybirdLock = new Core.SimpleHybirdLock( ); m_UpdateAction = new Action( Invalidate ); timer_alarm_check = new Timer( ); timer_alarm_check.Tick += Timer_alarm_check_Tick; timer_alarm_check.Interval = 1000; DoubleBuffered = true; } private void Timer_alarm_check_Tick( object sender, EventArgs e ) { if (value_current > value_alarm_max || value_current < value_alarm_min) { alarm_check = !alarm_check; } else { alarm_check = false; } Invalidate( ); } private void UserGaugeChart_Load( object sender, EventArgs e ) { timer_alarm_check.Start( ); } private void UserGaugeChart_Paint( object sender, PaintEventArgs e ) { if (!Authorization.nzugaydgwadawdibbas( )) return; Graphics g = e.Graphics; g.SmoothingMode = SmoothingMode.HighQuality; // 消除锯齿 g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.ClearTypeGridFit; // 优化文本显示 OperateResult setting = GetCenterPoint( ); if (!setting.IsSuccess) return; // 不满足绘制条件 Point center = setting.Content1; int radius = setting.Content2; float angle = Convert.ToSingle( setting.Content3 ); Rectangle circular = new Rectangle( -radius, -radius, 2 * radius, 2 * radius ); Rectangle circular_larger = new Rectangle( -radius - 5, -radius - 5, 2 * radius + 10, 2 * radius + 10 ); Rectangle circular_mini = new Rectangle( -radius / 3, -radius / 3, 2 * radius / 3, 2 * radius / 3 ); g.TranslateTransform( center.X, center.Y ); g.DrawArc( pen_gauge_border, circular_mini, -angle, angle * 2 - 180 ); g.DrawArc( pen_gauge_border, circular, angle - 180, 180 - angle * 2 ); g.DrawLine( pen_gauge_border, (int)(-(radius / 3) * Math.Cos( angle / 180 * Math.PI )), -(int)((radius / 3) * Math.Sin( angle / 180 * Math.PI )), -(int)((radius - 30) * Math.Cos( angle / 180 * Math.PI )), -(int)((radius - 30) * Math.Sin( angle / 180 * Math.PI )) ); g.DrawLine( pen_gauge_border, (int)((radius - 30) * Math.Cos( angle / 180 * Math.PI )), -(int)((radius - 30) * Math.Sin( angle / 180 * Math.PI )), (int)((radius / 3) * Math.Cos( angle / 180 * Math.PI )), -(int)((radius / 3) * Math.Sin( angle / 180 * Math.PI )) ); // 开始绘制刻度 g.RotateTransform( angle - 90 ); int totle = segment_count; for (int i = 0; i <= totle; i++) { Rectangle rect = new Rectangle( -2, -radius, 3, 7 ); g.FillRectangle( Brushes.DimGray, rect ); rect = new Rectangle( -50, -radius + 7, 100, 20 ); double current = ValueStart + (ValueMax - ValueStart) * i / totle; g.DrawString( current.ToString( ), Font, Brushes.DodgerBlue, rect, centerFormat ); g.RotateTransform( (180 - 2 * angle) / totle / 2 ); if (i != totle) g.DrawLine( Pens.DimGray, 0, -radius, 0, -radius + 3 ); g.RotateTransform( (180 - 2 * angle) / totle / 2 ); } g.RotateTransform( -(180 - 2 * angle) / totle ); g.RotateTransform( angle - 90 ); Rectangle text = new Rectangle( -36, -(radius * 2 / 3 - 3), 72, Font.Height + 3 ); // 如果处于报警中,就闪烁 if (value_current > value_alarm_max || value_current < value_alarm_min) { if (alarm_check) { g.FillRectangle( Brushes.OrangeRed, text ); } } // g.FillRectangle(Brushes.Wheat, text); if (IsTextUnderPointer) { g.DrawString( Value.ToString( ), Font, Brushes.DimGray, text, centerFormat ); // g.DrawRectangle(pen_gauge_border, text); text.Offset( 0, Font.Height ); if (!string.IsNullOrEmpty( UnitText )) { g.DrawString( UnitText, Font, Brushes.Gray, text, centerFormat ); } } g.RotateTransform( angle - 90 ); g.RotateTransform( (float)((value_paint - ValueStart) / (ValueMax - ValueStart) * (180 - 2 * angle)) ); Rectangle rectangle = new Rectangle( -5, -5, 10, 10 ); g.FillEllipse( brush_gauge_pointer, rectangle ); // g.DrawEllipse(Pens.Red, rectangle); Point[] points = new Point[] { new Point( 5, 0 ), new Point( 2, -radius + 40 ), new Point( 0, -radius + 20 ), new Point( -2, -radius + 40 ), new Point( -5, 0 ) }; g.FillPolygon( brush_gauge_pointer, points ); // g.DrawLines(Pens.Red, points); g.RotateTransform( (float)(-(value_paint - ValueStart) / (ValueMax - ValueStart) * (180 - 2 * angle)) ); g.RotateTransform( 90 - angle ); if (value_alarm_min > ValueStart && value_alarm_min <= ValueMax) { g.DrawArc( pen_gauge_alarm, circular_larger, angle - 180, (float)((ValueAlarmMin - ValueStart) / (ValueMax - ValueStart) * (180 - 2 * angle)) ); } if (value_alarm_max >= ValueStart && value_alarm_max < ValueMax) { float angle_max = (float)((value_alarm_max - ValueStart) / (ValueMax - ValueStart) * (180 - 2 * angle)); g.DrawArc( pen_gauge_alarm, circular_larger, -180 + angle + angle_max, 180 - 2 * angle - angle_max ); } if (!IsTextUnderPointer) { g.DrawString( Value.ToString( ), Font, Brushes.DimGray, text, centerFormat ); // g.DrawRectangle(pen_gauge_border, text); text.Offset( 0, Font.Height ); if (!string.IsNullOrEmpty( UnitText )) { g.DrawString( UnitText, Font, Brushes.Gray, text, centerFormat ); } } g.ResetTransform( ); } /// /// 获取中心点的坐标 /// /// private OperateResult GetCenterPoint( ) { OperateResult result = new OperateResult( ); if (Height <= 35) return result; if (Width <= 20) return result; result.IsSuccess = true; if (!IsBigSemiCircle) { // 以纵轴为标准创建的图像,特点是小于半圆~半圆变化 result.Content2 = Height - 30; if ((Width - 40) / 2d > result.Content2) { result.Content3 = Math.Acos( 1 ) * 180 / Math.PI; } else { result.Content3 = Math.Acos( (Width - 40) / 2d / (Height - 30) ) * 180 / Math.PI; } result.Content1 = new Point( Width / 2, Height - 10 ); return result; } else { // 以横轴为标准创建的图像,特点是半圆~整圆变化 result.Content2 = (Width - 40) / 2; if ((Height - 30) < result.Content2) { result.Content2 = Height - 30; result.Content3 = Math.Acos( 1 ) * 180 / Math.PI; result.Content1 = new Point( Width / 2, Height - 10 ); return result; } else { int left = Height - 30 - result.Content2; if (left > result.Content2) left = result.Content2; result.Content3 = -Math.Asin( left * 1.0d / result.Content2 ) * 180 / Math.PI; result.Content1 = new Point( Width / 2, result.Content2 + 20 ); return result; } } } private void ThreadPoolUpdateProgress( object obj ) { try { // 开始计算更新细节 int version = (int)obj; if (value_paint == value_current) return; double m_speed = Math.Abs( value_paint - value_current ) / 10; if (m_speed == 0) m_speed = 1; while (value_paint != value_current) { System.Threading.Thread.Sleep( 17 ); if (version != m_version) break; hybirdLock.Enter( ); double newActual = 0; if (value_paint > value_current) { double offect = value_paint - value_current; if (offect > m_speed) offect = m_speed; newActual = value_paint - offect; } else { double offect = value_current - value_paint; if (offect > m_speed) offect = m_speed; newActual = value_paint + offect; } value_paint = newActual; hybirdLock.Leave( ); if (version == m_version) { if (IsHandleCreated) Invoke( m_UpdateAction ); } else { break; } } } catch (Exception) { // BasicFramework.SoftBasic.ShowExceptionMessage(ex); } } #region Private Member private Color color_gauge_border = Color.DimGray; private Pen pen_gauge_border = null; // 绘制仪表盘的边框色 private Color color_gauge_pointer = Color.Tomato; private Brush brush_gauge_pointer = null; // 绘制仪表盘的指针的颜色 private double value_start = 0; // 仪表盘的初始值 private double value_max = 100d; // 仪表盘的结束值 private double value_current = 0d; // 仪表盘的当前值 private double value_alarm_max = 80d; // 仪表盘的上限报警值 private double value_alarm_min = 20d; // 仪表盘的下限报警值 private Pen pen_gauge_alarm = null; // 绘制仪表盘的报警区间的虚线画笔 private int m_version = 0; // 设置数据时的版本,用于更新时的版本验证 private double value_paint = 0d; // 绘制图形时候的中间值 private Core.SimpleHybirdLock hybirdLock; // 数据的同步锁 private Action m_UpdateAction; // 更新界面的委托 private Timer timer_alarm_check; // 数据处于危险区域的报警闪烁 private bool alarm_check = false; // 每秒计时的报警反馈 private int segment_count = 10; // 显示区域的分割片段 private StringFormat centerFormat = null; // 居中显示的格式化文本 private string value_unit_text = string.Empty; // 数值的单位,可以设置并显示 private bool text_under_pointer = true; // 指示文本是否在指针的下面 private bool isBigSemiCircle = false; // 是否显示超过半个圆的信息 #endregion #region Public Member /// /// 获取或设置仪表盘的背景色 /// [Browsable( true )] [Category( "外观" )] [Description( "获取或设置仪表盘的背景色" )] [DefaultValue( typeof( Color ), "DimGray" )] public Color GaugeBorder { get { return color_gauge_border; } set { pen_gauge_border?.Dispose( ); pen_gauge_border = new Pen( value ); color_gauge_border = value; Invalidate( ); } } /// /// 获取或设置指针的颜色 /// [Browsable( true )] [Category( "外观" )] [Description( "获取或设置仪表盘指针的颜色" )] [DefaultValue( typeof( Color ), "Tomato" )] public Color PointerColor { get { return color_gauge_pointer; } set { brush_gauge_pointer?.Dispose( ); brush_gauge_pointer = new SolidBrush( value ); color_gauge_pointer = value; Invalidate( ); } } /// /// 获取或设置数值的起始值,默认为0 /// [Browsable( true )] [Category( "外观" )] [Description( "获取或设置数值的起始值,默认为0" )] [DefaultValue( 0d )] public double ValueStart { get { if (value_max <= value_start) { return value_start + 1; } else { return value_start; } } set { value_start = value; Invalidate( ); } } /// /// 获取或设置数值的最大值,默认为100 /// [Browsable( true )] [Category( "外观" )] [Description( "获取或设置数值的最大值,默认为100" )] [DefaultValue( 100d )] public double ValueMax { get { if (value_max <= value_start) { return value_start + 1; } else { return value_max; } } set { value_max = value; Invalidate( ); } } /// /// 获取或设置数值的当前值,应该处于最小值和最大值之间 /// [Browsable( true )] [Category( "外观" )] [Description( "获取或设置数值的当前值,默认为0" )] [DefaultValue( 0d )] public double Value { get { return value_current; } set { if (ValueStart <= value && value <= ValueMax) { if (value != value_current) { value_current = value; int version = System.Threading.Interlocked.Increment( ref m_version ); System.Threading.ThreadPool.QueueUserWorkItem( new System.Threading.WaitCallback( ThreadPoolUpdateProgress ), version ); } } } } /// /// 获取或设置数值的上限报警值,设置为超过最大值则无上限报警 /// [Browsable( true )] [Category( "外观" )] [Description( "获取或设置数值的上限报警值,设置为超过最大值则无上限报警,默认为80" )] [DefaultValue( 80d )] public double ValueAlarmMax { get { return value_alarm_max; } set { if (ValueStart <= value) { value_alarm_max = value; Invalidate( ); } } } /// /// 获取或设置数值的下限报警值,设置为超过最大值则无上限报警 /// [Browsable( true )] [Category( "外观" )] [Description( "获取或设置数值的下限报警值,设置为小于最小值则无下限报警,默认为20" )] [DefaultValue( 20d )] public double ValueAlarmMin { get { return value_alarm_min; } set { if (value <= ValueMax) { value_alarm_min = value; Invalidate( ); } } } /// /// 获取或设置仪表盘的分割段数,最小为2,最大1000 /// [Browsable( true )] [Category( "外观" )] [Description( "获取或设置仪表盘的分割段数,最小为2,最大1000" )] [DefaultValue( 10 )] public int SegmentCount { get { return segment_count; } set { if (value > 1 && value < 1000) { segment_count = value; } } } /// /// 获取或设置仪表盘的单位描述文本 /// [Browsable( true )] [Category( "外观" )] [Description( "获取或设置仪表盘的单位描述文本" )] [DefaultValue( "" )] public string UnitText { get { return value_unit_text; } set { value_unit_text = value; Invalidate( ); } } /// /// 获取或设置文本是否是指针的下面 /// [Browsable( true )] [Category( "外观" )] [Description( "获取或设置文本是否是指针的下面" )] [DefaultValue( true )] public bool IsTextUnderPointer { get { return text_under_pointer; } set { text_under_pointer = value; Invalidate( ); } } /// /// 通常情况,仪表盘不会大于半个圆,除非本属性设置为 True /// [Browsable( true )] [Category( "外观" )] [Description( "通常情况,仪表盘不会大于半个圆,除非本属性设置为 True" )] [DefaultValue( false )] public bool IsBigSemiCircle { get { return isBigSemiCircle; } set { isBigSemiCircle = value; Invalidate( ); } } #endregion } }