You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

572 lines
20 KiB
C#

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

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 )
{
if (!Authorization.nzugaydgwadawdibbas( )) return;
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 > 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( );
}
/// <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;
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
/// <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
{
if (value_max <= value_start)
{
return value_start + 1;
}
else
{
return value_start;
}
}
set
{
value_start = value;
Invalidate( );
}
}
/// <summary>
/// 获取或设置数值的最大值默认为100
/// </summary>
[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( );
}
}
/// <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( );
}
}
/// <summary>
/// 通常情况,仪表盘不会大于半个圆,除非本属性设置为 True
/// </summary>
[Browsable( true )]
[Category( "外观" )]
[Description( "通常情况,仪表盘不会大于半个圆,除非本属性设置为 True" )]
[DefaultValue( false )]
public bool IsBigSemiCircle
{
get
{
return isBigSemiCircle;
}
set
{
isBigSemiCircle = value;
Invalidate( );
}
}
#endregion
}
}