| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519 |
- 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
- {
- /// <summary>
- /// 仪表盘控件类
- /// </summary>
- public partial class UserGaugeChart : UserControl
- {
- /// <summary>
- /// 实例化一个仪表盘控件
- /// </summary>
- 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 )
- {
- Graphics g = e.Graphics;
- g.SmoothingMode = SmoothingMode.HighQuality; // 消除锯齿
- g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.ClearTypeGridFit; // 优化文本显示
- OperateResult<Point, int, double> 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 > value_start && value_alarm_min <= value_max)
- {
- g.DrawArc( pen_gauge_alarm, circular_larger, angle - 180, (float)((ValueAlarmMin - ValueStart) / (ValueMax - ValueStart) * (180 - 2 * angle)) );
- }
- if (value_alarm_max >= value_start && value_alarm_max < value_max)
- {
- 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( );
- }
- /// <summary>
- /// 获取中心点的坐标
- /// </summary>
- /// <returns></returns>
- private OperateResult<Point, int, double> GetCenterPoint( )
- {
- OperateResult<Point, int, double> result = new OperateResult<Point, int, double>( );
- if (Height <= 35) return result;
- if (Width <= 20) return result;
- result.IsSuccess = true;
- 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;
- }
- 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 ex)
- {
- // 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; // 指示文本是否在指针的下面
- #endregion
- #region Public Member
- /// <summary>
- /// 获取或设置仪表盘的背景色
- /// </summary>
- [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( );
- }
- }
- /// <summary>
- /// 获取或设置指针的颜色
- /// </summary>
- [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( );
- }
- }
- /// <summary>
- /// 获取或设置数值的起始值,默认为0
- /// </summary>
- [Browsable( true )]
- [Category( "外观" )]
- [Description( "获取或设置数值的起始值,默认为0" )]
- [DefaultValue( 0d )]
- public double ValueStart
- {
- get
- {
- return value_start;
- }
- set
- {
- value_start = value;
- if (value_max <= value_start)
- {
- value_max = value_start + 1;
- }
- Invalidate( );
- }
- }
- /// <summary>
- /// 获取或设置数值的最大值,默认为100
- /// </summary>
- [Browsable( true )]
- [Category( "外观" )]
- [Description( "获取或设置数值的最大值,默认为100" )]
- [DefaultValue( 100d )]
- public double ValueMax
- {
- get
- {
- return value_max;
- }
- set
- {
- value_max = value;
- if (value_max <= value_start)
- {
- value_max = value_start + 1;
- }
- Invalidate( );
- }
- }
- /// <summary>
- /// 获取或设置数值的当前值,应该处于最小值和最大值之间
- /// </summary>
- [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 );
- }
- }
- }
- }
- /// <summary>
- /// 获取或设置数值的上限报警值,设置为超过最大值则无上限报警
- /// </summary>
- [Browsable( true )]
- [Category( "外观" )]
- [Description( "获取或设置数值的上限报警值,设置为超过最大值则无上限报警,默认为80" )]
- [DefaultValue( 80d )]
- public double ValueAlarmMax
- {
- get
- {
- return value_alarm_max;
- }
- set
- {
- if (ValueStart <= value)
- {
- value_alarm_max = value;
- Invalidate( );
- }
- }
- }
- /// <summary>
- /// 获取或设置数值的下限报警值,设置为超过最大值则无上限报警
- /// </summary>
- [Browsable( true )]
- [Category( "外观" )]
- [Description( "获取或设置数值的下限报警值,设置为小于最小值则无下限报警,默认为20" )]
- [DefaultValue( 20d )]
- public double ValueAlarmMin
- {
- get
- {
- return value_alarm_min;
- }
- set
- {
- if (value <= ValueMax)
- {
- value_alarm_min = value;
- Invalidate( );
- }
- }
- }
- /// <summary>
- /// 获取或设置仪表盘的分割段数,最小为2,最大1000
- /// </summary>
- [Browsable( true )]
- [Category( "外观" )]
- [Description( "获取或设置仪表盘的分割段数,最小为2,最大1000" )]
- [DefaultValue( 10 )]
- public int SegmentCount
- {
- get
- {
- return segment_count;
- }
- set
- {
- if (value > 1 && value < 1000)
- {
- segment_count = value;
- }
- }
- }
- /// <summary>
- /// 获取或设置仪表盘的单位描述文本
- /// </summary>
- [Browsable( true )]
- [Category( "外观" )]
- [Description( "获取或设置仪表盘的单位描述文本" )]
- [DefaultValue( "" )]
- public string UnitText
- {
- get
- {
- return value_unit_text;
- }
- set
- {
- value_unit_text = value;
- Invalidate( );
- }
- }
- /// <summary>
- /// 获取或设置文本是否是指针的下面
- /// </summary>
- [Browsable( true )]
- [Category( "外观" )]
- [Description( "获取或设置文本是否是指针的下面" )]
- [DefaultValue( true )]
- public bool IsTextUnderPointer
- {
- get
- {
- return text_under_pointer;
- }
- set
- {
- text_under_pointer = value;
- Invalidate( );
- }
- }
- #endregion
- }
- }
|