WPF绘制仪表

1,效果:

WPF绘制仪表

2,代码:

2.1,资源字典:BaseStyle.xaml

  
    
        #190E1C
    
    
    
    
    #23153B
    #23153B
    #45246B 
    
    #00C9FF
    #00C9FF
    #00C9F0
2.2,自定义圆弧类Arc
//WPF未提供圆弧Shape,故根据Ellipse实现过程自定义一个圆弧类
 class Arc : Shape
    {
        private Rect _rect = Rect.Empty;
        public double StartAngle
        {
            get { return (double)GetValue(StartAngleProperty); }
            set { SetValue(StartAngleProperty, value); }
        }
        // Using a DependencyProperty as the backing store for StartAngle.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty StartAngleProperty =
            DependencyProperty.Register("StartAngle", typeof(double), typeof(Arc), new FrameworkPropertyMetadata((double)-135, FrameworkPropertyMetadataOptions.AffectsRender));
        public double EndAngle
        {
            get { return (double)GetValue(EndAngleProperty); }
            set { SetValue(EndAngleProperty, value); }
        }
        // Using a DependencyProperty as the backing store for EndAngle.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty EndAngleProperty =
            DependencyProperty.Register("EndAngle", typeof(double), typeof(Arc), new FrameworkPropertyMetadata((double)135, FrameworkPropertyMetadataOptions.AffectsRender));
        public double ArcThickness
        {
            get { return (double)GetValue(ArcThicknessProperty); }
            set { SetValue(ArcThicknessProperty, value); }
        }
        // Using a DependencyProperty as the backing store for ArcThickness.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty ArcThicknessProperty =
            DependencyProperty.Register("ArcThickness", typeof(double), typeof(Arc), new FrameworkPropertyMetadata((double)10, FrameworkPropertyMetadataOptions.AffectsRender));
        public override Geometry RenderedGeometry => DefiningGeometry;
        public override Transform GeometryTransform => Transform.Identity;
        protected override Geometry DefiningGeometry
        {
            get
            {
                if (_rect.IsEmpty)
                {
                    return Geometry.Empty;
                }
                PathGeometry path = new PathGeometry();
                PathFigure figure = new PathFigure();
                figure.IsClosed = false;
                //根据起始角度算出起始点
                //可绘图的区域为_rec
                double offsetAngle = 270;
                double diameter = _rect.Width > _rect.Height ? _rect.Height : _rect.Width;
                Point centerPoint = new Point(_rect.X + _rect.Width / 2, _rect.Y + _rect.Height / 2);
                double xoffset = diameter * Math.Cos((StartAngle + offsetAngle) / 180 * Math.PI) / 2;
                double yoffset = diameter * Math.Sin((StartAngle + offsetAngle) / 180 * Math.PI) / 2;
                Point startPoint = new Point(centerPoint.X + xoffset, centerPoint.Y + yoffset);
                double xoffset2 = diameter * Math.Cos((EndAngle + offsetAngle) / 180 * Math.PI) / 2;
                double yoffset2 = diameter * Math.Sin((EndAngle + offsetAngle) / 180 * Math.PI) / 2;
                Point endPoint = new Point(centerPoint.X + xoffset2, centerPoint.Y + yoffset2);
                figure.StartPoint = startPoint;
                ArcSegment arc = new ArcSegment
                {
                    Point = endPoint,
                    Size = new Size(diameter / 2, diameter / 2),
                    SweepDirection = SweepDirection.Clockwise
                };
                if (EndAngle - StartAngle < 180)
                {
                    arc.IsLargeArc = false;
                }
                else
                {
                    arc.IsLargeArc = true;
                }
                figure.Segments.Add(arc);
                path.Figures.Add(figure);
                return path;
            }
        }
        static Arc()
        {
            Shape.StretchProperty.OverrideMetadata(typeof(Arc), new FrameworkPropertyMetadata(Stretch.Fill));
        }
        protected override Size MeasureOverride(Size constraint)
        {
            double width = constraint.Width;
            double height = constraint.Height;
            if (double.IsInfinity(width) && double.IsInfinity(height))
            {
                return GetNaturalSize();
            }
            if (base.Stretch == Stretch.UniformToFill)
            {
                width = ((!double.IsInfinity(width) && !double.IsInfinity(height)) ? Math.Max(width, height) : Math.Min(width, height));
                return new Size(width, width);
            }
            return constraint;
        }
        protected override Size ArrangeOverride(Size finalSize)
        {
            double strokeThickness = ArcThickness;
            double num = strokeThickness / 2.0;
            _rect = new Rect(num, num, Math.Max(0.0, finalSize.Width - strokeThickness), Math.Max(0.0, finalSize.Height - strokeThickness));
            switch (base.Stretch)
            {
                case Stretch.None:
                    {
                        ref Rect rect = ref _rect;
                        double width = (_rect.Height = 0.0);
                        rect.Width = width;
                        break;
                    }
                case Stretch.Uniform:
                    if (_rect.Width > _rect.Height)
                    {
                        _rect.Width = _rect.Height;
                    }
                    else
                    {
                        _rect.Height = _rect.Width;
                    }
                    break;
                case Stretch.UniformToFill:
                    if (_rect.Width < _rect.Height)
                    {
                        _rect.Width = _rect.Height;
                    }
                    else
                    {
                        _rect.Height = _rect.Width;
                    }
                    break;
            }
            return finalSize;
            // return base.ArrangeOverride(finalSize);
        }
        protected override void OnRender(DrawingContext drawingContext)
        {
            if (!_rect.IsEmpty)
            {
                Pen pen = new Pen();
                pen.Brush = Stroke;
                pen.Thickness = ArcThickness;
                pen.StartLineCap = StrokeStartLineCap;
                pen.EndLineCap = StrokeEndLineCap;
                drawingContext.DrawGeometry(base.Fill, pen, RenderedGeometry);
            }
        }
        Size GetNaturalSize()
        {
            double strokeThickness = GetStrokeThickness();
            return new Size(strokeThickness * 2, strokeThickness * 2);
        }
        double GetStrokeThickness()
        {
            if (double.IsNaN(ArcThickness) || double.IsInfinity(ArcThickness))
            {
                return 0.0;
            }
            else
            {
                return ArcThickness;
            }
        }
    }
2.3,自定义仪表控件xaml

    
        
    
    
        
            
                
                    
                    
                    
                    
                    
                        
                            
                                
                                
                                
                            
                        
                    
                    
                        
                            
                                
                                
                                
                            
                        
                        
                            
                                
                                
                            
                        
                    
                    
                        
                            
                                
                                
                                
                            
                        
                        
                            
                            
                        
                    
                
            
        
    

效果:

WPF绘制仪表

2.4,仪表控件代码:
  public partial class MeterPlate : UserControl
    {
        const double OffsetAngle = 270;
        public static readonly DependencyProperty StartAngleProperty;
        public static readonly DependencyProperty EndAngleProperty;
        public static readonly DependencyProperty ValueProperty;
        public static readonly DependencyProperty MinNumProperty;
        public static readonly DependencyProperty MaxNumProperty;
        public static readonly DependencyProperty PlateBackgroundProperty;
        public static readonly DependencyProperty PlateBorderBrushProperty;
        public static readonly DependencyProperty PlateBorderThicknessProperty;
        public static readonly DependencyProperty ArcThicknessProperty;
        public static readonly DependencyProperty LowAlarmPercentProperty;
        public static readonly DependencyProperty HeightAlarmPercentProperty;
        public static readonly DependencyProperty LowAlarmColorProperty;
        public static readonly DependencyProperty HeightAlarmColorProperty;
        public MeterPlate()
        {
            InitializeComponent();
        }
        static MeterPlate()
        {
            StartAngleProperty =
            DependencyProperty.Register("StartAngle", typeof(double), typeof(MeterPlate), new FrameworkPropertyMetadata((double)-135, FrameworkPropertyMetadataOptions.AffectsRender));
            EndAngleProperty =
                DependencyProperty.Register("EndAngle", typeof(double), typeof(MeterPlate), new FrameworkPropertyMetadata((double)135, FrameworkPropertyMetadataOptions.AffectsRender));
            ValueProperty =
                 DependencyProperty.Register("Value", typeof(double), typeof(MeterPlate),
                      new FrameworkPropertyMetadata((double)45, FrameworkPropertyMetadataOptions.AffectsRender));
            MinNumProperty =
                 DependencyProperty.Register("MinNum", typeof(double), typeof(MeterPlate),
                     new FrameworkPropertyMetadata((double)0, FrameworkPropertyMetadataOptions.AffectsRender));
            MaxNumProperty =
                DependencyProperty.Register("MaxNum", typeof(double), typeof(MeterPlate),
                    new FrameworkPropertyMetadata((double)120, FrameworkPropertyMetadataOptions.AffectsRender));
            PlateBackgroundProperty =
                 DependencyProperty.Register("PlateBackground", typeof(Brush), typeof(MeterPlate), new PropertyMetadata(new SolidColorBrush(Color.FromRgb(43, 26, 81))));
            PlateBorderBrushProperty =
                DependencyProperty.Register("PlateBorderBrush", typeof(Brush), typeof(MeterPlate), null);
            PlateBorderThicknessProperty =
                 DependencyProperty.Register("PlateBorderThickness", typeof(Thickness), typeof(MeterPlate), new PropertyMetadata(new Thickness(0)));
            ArcThicknessProperty =
            DependencyProperty.Register("ArcThickness", typeof(double), typeof(MeterPlate), new PropertyMetadata((double)30));
            LowAlarmPercentProperty =
            DependencyProperty.Register("LowAlarmPercent", typeof(double), typeof(MeterPlate), new FrameworkPropertyMetadata((double)1, FrameworkPropertyMetadataOptions.AffectsRender));
            HeightAlarmPercentProperty =
            DependencyProperty.Register(" HeightAlarmPercent", typeof(double), typeof(MeterPlate), new FrameworkPropertyMetadata((double)1, FrameworkPropertyMetadataOptions.AffectsRender));
            LowAlarmColorProperty =
            DependencyProperty.Register("LowAlarmColor", typeof(Color), typeof(MeterPlate), new FrameworkPropertyMetadata(Colors.Orange, FrameworkPropertyMetadataOptions.AffectsRender));
            HeightAlarmColorProperty =
            DependencyProperty.Register("HeightAlarmColor", typeof(Color), typeof(MeterPlate), new FrameworkPropertyMetadata(Colors.Red, FrameworkPropertyMetadataOptions.AffectsRender));
        }
        [Browsable(true), Category("自定义属性"), Description("仪表盘开始角度")]
        public double StartAngle
        {
            get { return (double)GetValue(StartAngleProperty); }
            set { SetValue(StartAngleProperty, value); }
        }
        [Browsable(true), Category("自定义属性"), Description("仪表盘结束角度")]
        public double EndAngle
        {
            get { return (double)GetValue(EndAngleProperty); }
            set { SetValue(EndAngleProperty, value); }
        }
        [Browsable(true), Category("自定义属性"), Description("当前值")]
        public double Value
        {
            get { return (double)GetValue(ValueProperty); }
            set { SetValue(ValueProperty, value); }
        }
        [Browsable(true), Category("自定义属性"), Description("仪表盘最小值")]
        public double MinNum
        {
            get { return (double)GetValue(MinNumProperty); }
            set { SetValue(MinNumProperty, value); }
        }
        [Browsable(true), Category("自定义属性"), Description("仪表盘最大值")]
        public double MaxNum
        {
            get { return (double)GetValue(MaxNumProperty); }
            set { SetValue(MaxNumProperty, value); }
        }
        [Browsable(true), Category("自定义属性"), Description("仪表盘低值区域所占区域默认为1")]
        public double LowAlarmPercent
        {
            get { return (double)GetValue(LowAlarmPercentProperty); }
            set { SetValue(LowAlarmPercentProperty, value); }
        }
        [Browsable(true), Category("自定义属性"), Description("仪表盘高值区域所占区域默认为1")]
        public double HeightAlarmPercent
        {
            get { return (double)GetValue(HeightAlarmPercentProperty); }
            set { SetValue(HeightAlarmPercentProperty, value); }
        }
        [Browsable(true), Category("自定义属性"), Description("仪表盘低值区域刻度值与刻度线颜色")]
        public Color LowAlarmColor
        {
            get { return (Color)GetValue(LowAlarmColorProperty); }
            set { SetValue(LowAlarmColorProperty, value); }
        }
        [Browsable(true), Category("自定义属性"), Description("仪表盘高值区域刻度值与刻度线颜色")]
        public Color HeightAlarmColor
        {
            get { return (Color)GetValue(HeightAlarmColorProperty); }
            set { SetValue(HeightAlarmColorProperty, value); }
        }
        [Browsable(true), Category("自定义属性"), Description("仪表盘背景画刷")]
        public Brush PlateBackground
        {
            get { return (Brush)GetValue(PlateBackgroundProperty); }
            set { SetValue(PlateBackgroundProperty, value); }
        }
        [Browsable(true), Category("自定义属性"), Description("仪表盘边框画刷")]
        public Brush PlateBorderBrush
        {
            get { return (Brush)GetValue(PlateBorderBrushProperty); }
            set { SetValue(PlateBorderBrushProperty, value); }
        }
        [Browsable(true), Category("自定义属性"), Description("仪表盘边框宽度")]
        public Thickness PlateBorderThickness
        {
            get { return (Thickness)GetValue(PlateBorderThicknessProperty); }
            set { SetValue(PlateBorderThicknessProperty, value); }
        }
        [Browsable(true), Category("自定义属性"), Description("仪表盘进度条宽度")]
        public double ArcThickness
        {
            get { return (double)GetValue(ArcThicknessProperty); }
            set { SetValue(ArcThicknessProperty, value); }
        }
        protected override void OnRender(DrawingContext drawingContext)
        {
            base.OnRender(drawingContext);
            DrawScale();
            DrawAngle();
        }
        /// 
        /// 画表盘的刻度
        /// 
        private void DrawScale()
        {
            double unitAngle = (EndAngle - StartAngle) / (MaxNum - MinNum);
            double realStartAngle = StartAngle + OffsetAngle;
            double outgap = arc01.ArcThickness;
            this.canvasPlate.Children.Clear();
            Point centerPoint = new Point(200, 200);
            if (!double.IsInfinity(border01.BorderThickness.Left) && !double.IsNaN(border01.BorderThickness.Left))
            {
                centerPoint = new Point(200 - border01.BorderThickness.Left, 200 - border01.BorderThickness.Left);
            }
            for (double i = 0; i <= this.MaxNum - this.MinNum; i++)
            {
                //添加刻度线
                Line lineScale = new Line();
                if (LowAlarmPercent != 0 && i <= LowAlarmPercent * 10)
                {
                    lineScale.Stroke = new SolidColorBrush(LowAlarmColor);
                }
                else if (HeightAlarmPercent != 0 && i >= (MaxNum - HeightAlarmPercent * 10))
                {
                    lineScale.Stroke = new SolidColorBrush(HeightAlarmColor);
                }
                else
                {
                    lineScale.Stroke = new SolidColorBrush(Colors.White);
                    if (i % 10 != 0)
                    {
                        lineScale.Opacity = 0.6;
                    }
                }
                if (i % 10 == 0)
                {
                    //注意Math.Cos和Math.Sin的参数是弧度,记得将角度转为弧度制
                    lineScale.X1 = centerPoint.X + (centerPoint.X - outgap - 20) * Math.Cos((i * unitAngle + realStartAngle) * Math.PI / 180);
                    lineScale.Y1 = centerPoint.Y + (centerPoint.X - outgap - 20) * Math.Sin((i * unitAngle + realStartAngle) * Math.PI / 180);
                    lineScale.StrokeThickness = 2;
                    //添加刻度值
                    TextBlock txtScale = new TextBlock();
                    txtScale.Text = (i + this.MinNum).ToString();
                    txtScale.Width = 34;
                    txtScale.TextAlignment = TextAlignment.Center;
                    if (LowAlarmPercent != 0 && i <= LowAlarmPercent * 10)
                    {
                        txtScale.Foreground = new SolidColorBrush(LowAlarmColor);
                    }
                    else if (HeightAlarmPercent != 0 && i >= (MaxNum - HeightAlarmPercent * 10))
                    {
                        txtScale.Foreground = new SolidColorBrush(HeightAlarmColor);
                    }
                    else
                    {
                        txtScale.Foreground = new SolidColorBrush(Colors.White);
                    }
                    txtScale.RenderTransform = new RotateTransform() { Angle = 0, CenterX = 17, CenterY = 8 };
                    txtScale.FontSize = 18;
                    if (i > (MaxNum - MinNum) / 2)
                    {
                        Canvas.SetLeft(txtScale, centerPoint.X + (centerPoint.X - outgap - 35) * Math.Cos((i * unitAngle + realStartAngle) * Math.PI / 180) - 20);
                        Canvas.SetTop(txtScale, centerPoint.Y + (centerPoint.X - outgap - 35) * Math.Sin((i * unitAngle + realStartAngle) * Math.PI / 180) - 10);
                    }
                    else
                    {
                        Canvas.SetLeft(txtScale, centerPoint.X + (centerPoint.X - outgap - 35) * Math.Cos((i * unitAngle + realStartAngle) * Math.PI / 180) - 17);
                        Canvas.SetTop(txtScale, centerPoint.Y + (centerPoint.X - outgap - 35) * Math.Sin((i * unitAngle + realStartAngle) * Math.PI / 180) - 10);
                    }
                    this.canvasPlate.Children.Add(txtScale);
                }
                else
                {
                    lineScale.X1 = centerPoint.X + (centerPoint.X - outgap - 10) * Math.Cos((i * unitAngle + realStartAngle) * Math.PI / 180);
                    lineScale.Y1 = centerPoint.Y + (centerPoint.X - outgap - 10) * Math.Sin((i * unitAngle + realStartAngle) * Math.PI / 180);
                    lineScale.StrokeThickness = 1;
                    // lineScale.Opacity = 0.6;
                }
                lineScale.X2 = centerPoint.X + (centerPoint.X - outgap) * Math.Cos((i * unitAngle + realStartAngle) * Math.PI / 180);
                lineScale.Y2 = centerPoint.Y + (centerPoint.X - outgap) * Math.Sin((i * unitAngle + realStartAngle) * Math.PI / 180);
                this.canvasPlate.Children.Add(lineScale);
            }
            if (LowAlarmPercent != 0 && Value <= LowAlarmPercent * 10)
            {
                txtCurValue.Foreground = new SolidColorBrush(LowAlarmColor);
            }
            else if (HeightAlarmPercent != 0 && Value >= (MaxNum - HeightAlarmPercent * 10))
            {
                txtCurValue.Foreground = new SolidColorBrush(HeightAlarmColor);
            }
            else
            {
                txtCurValue.Foreground = new SolidColorBrush(Colors.White);
            }
        }
        private void DrawAngle()
        {
            double step = (Value - MinNum) / (this.MaxNum - this.MinNum) * (EndAngle - StartAngle) + OffsetAngle + StartAngle;
            double desAngle = (Value - MinNum) / (this.MaxNum - this.MinNum) * (EndAngle - StartAngle) + StartAngle;
            if (Value < MinNum)
            {
                step = OffsetAngle + StartAngle;
                desAngle = StartAngle;
            }
            if (Value > MaxNum)
            {
                step = OffsetAngle + EndAngle;
                desAngle = EndAngle;
            }
            DoubleAnimation da = new DoubleAnimation(step, new Duration(TimeSpan.FromMilliseconds(200)));
            this.rtPointer.BeginAnimation(RotateTransform.AngleProperty, da);
            //进度条显示
            DoubleAnimation da2 = new DoubleAnimation(desAngle, new Duration(TimeSpan.FromMilliseconds(200)));
            this.arckd.BeginAnimation(Arc.EndAngleProperty, da2);
        }
    }

3,应用:


    
      
            
                
                
            
            
                
                
            
            
            
                
                    
                    
                    
                    
                
                
                    
                    
                
                
                

            

        

效果

WPF绘制仪表

?测试代码:

  public partial class MainWindow : Window
    {
        public double Value
        {
            get { return (double)GetValue(ValueProperty); }
            set { SetValue(ValueProperty, value); }
        }
        // Using a DependencyProperty as the backing store for Value.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty ValueProperty =
            DependencyProperty.Register("Value", typeof(double), typeof(MainWindow), new PropertyMetadata((double)0
                ));
        public MainWindow()
        {
            InitializeComponent();
            this.DataContext = this;

        }
        bool isBreak = false;
        private void btnStart_Click(object sender, RoutedEventArgs e)
        {
            if (btnStart.Content.ToString().Equals("开始"))
            {
                isBreak = false;
                DoWork();
            }
            else
            {
                isBreak = true;
            }
        }
        Random random = new Random();
        async void DoWork()
        {
            btnStart.Content = "运行中";
            await Task.Run(() =>
            {
                while (true)
                {
                    if (isBreak)
                    {
                        break;
                    }
                    Dispatcher.Invoke(() =>
                    {
                        Value+=random.Next(0,20);
                        if (Value >= 120)
                        {
                            Value = 0;
                        }
                    });
                    System.Threading.Thread.Sleep(500);
                }
            });
            btnStart.Content = "停止";
            await Task.Delay(2000);
            btnStart.Content = "开始";
        }
    }

4,Demo链接:

https://download.csdn.net/download/lingxiao16888/89879311

上一篇:Linux镜像最全版本下载网站
下一篇:i38100多少钱(玩逆战电脑配置多少钱2020年版多少钱)