zoukankan      html  css  js  c++  java
  • Silverlight学习笔记十六自定义控件之一个漂亮的Gauge(仪表盘)

      我们可以在Silverlight可以自己定义我们需要的控件。这一节是自定义的Gauge控件(仪表)。呵呵翻译的不是很好。

    下面是演示效果:

    一.自定义控件步骤:

    步骤1:CircularGaugeControl用于定义控件的属性

    using System;
    using System.Net;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Documents;
    using System.Windows.Ink;
    using System.Windows.Input;
    using System.Windows.Media;
    using System.Windows.Media.Animation;
    using System.Windows.Shapes;

    namespace CircularGauge
    {
        /// <summary>
        /// 自定义控件-测量仪表(Gauge)
        /// </summary>
        [TemplatePart(Name = "LayoutRoot", Type = typeof(Grid))]
        [TemplatePart(Name = "Pointer", Type = typeof(Path))]
        [TemplatePart(Name = "RangeIndicatorLight", Type = typeof(Ellipse))]
        [TemplatePart(Name = "PointerCap", Type = typeof(Ellipse))]
        public class CircularGaugeControl : Control
        {
            #region 私有变量

            private Grid rootGrid;

            private Path rangeIndicator;
            private Path pointer;

            private Ellipse pointerCap;
            private Ellipse lightIndicator;

            private bool isInitialValueSet = false;
            private Double arcradius1;
            private Double arcradius2;
            private int animatingSpeedFactor = 6;

            #endregion

            #region 注入属性

            /// <summary>
            /// 获取或设置当前属性的值
            /// </summary>
            public static readonly DependencyProperty CurrentValueProperty =
                DependencyProperty.Register("CurrentValue", typeof(double), typeof(CircularGaugeControl),
                   new PropertyMetadata(Double.MinValue, new PropertyChangedCallback(CircularGaugeControl.OnCurrentValuePropertyChanged)));

            /// <summary>
            /// 获取或设置当前属性的最小值
            /// </summary>
            public static readonly DependencyProperty MinValueProperty =
                  DependencyProperty.Register("MinValue", typeof(double), typeof(CircularGaugeControl), null);

            /// <summary>
            /// 获取或设置当前属性的最大值
            /// </summary>
            public static readonly DependencyProperty MaxValueProperty =
                   DependencyProperty.Register("MaxValue", typeof(double), typeof(CircularGaugeControl), null);

            /// <summary>
            /// 获取或设置仪表半径的值
            /// </summary>
            public static readonly DependencyProperty RadiusProperty =
                  DependencyProperty.Register("Radius", typeof(double), typeof(CircularGaugeControl), null);

            /// <summary>
            /// 获取或设置指针半径的值
            /// </summary>
            public static readonly DependencyProperty PointerCapRadiusProperty =
                   DependencyProperty.Register("PointerCapRadius", typeof(double), typeof(CircularGaugeControl), null);

            /// <summary>
            ///  获取或设置指针的长度
            /// </summary>
            public static readonly DependencyProperty PointerLengthProperty =
                 DependencyProperty.Register("PointerLength", typeof(double), typeof(CircularGaugeControl), null);

            /// <summary>
            /// 获取或设置刻度的半径
            /// </summary>
            public static readonly DependencyProperty ScaleRadiusProperty =
                 DependencyProperty.Register("ScaleRadius", typeof(double), typeof(CircularGaugeControl), null);

            /// <summary>
            /// 获取或设置刻度的起始角度
            /// </summary>
            public static readonly DependencyProperty ScaleStartAngleProperty =
                  DependencyProperty.Register("ScaleStartAngle", typeof(double), typeof(CircularGaugeControl), null);

            /// <summary>
            /// 获取或设置刻度的摆动角度
            /// </summary>
            public static readonly DependencyProperty ScaleSweepAngleProperty =
                 DependencyProperty.Register("ScaleSweepAngle", typeof(double), typeof(CircularGaugeControl), null);

            /// <summary>
            /// 获取或设置主要的分布点个数
            /// </summary>
            public static readonly DependencyProperty MajorDivisionsCountProperty =
                 DependencyProperty.Register("MajorDivisionsCount", typeof(double), typeof(CircularGaugeControl), null);

            /// <summary>
            /// 获取或设置辅助的分布点个数
            /// </summary>
            public static readonly DependencyProperty MinorDivisionsCountProperty =
                 DependencyProperty.Register("MinorDivisionsCount", typeof(double), typeof(CircularGaugeControl), null);

            /// <summary>
            /// 优化
            /// </summary>
            public static readonly DependencyProperty OptimalRangeEndValueProperty =
                 DependencyProperty.Register("OptimalRangeEndValue", typeof(double), typeof(CircularGaugeControl), new PropertyMetadata(new PropertyChangedCallback(CircularGaugeControl.OnOptimalRangeEndValuePropertyChanged)));

            public static readonly DependencyProperty OptimalRangeStartValueProperty =
                DependencyProperty.Register("OptimalRangeStartValue", typeof(double), typeof(CircularGaugeControl), new PropertyMetadata(new PropertyChangedCallback(CircularGaugeControl.OnOptimalRangeStartValuePropertyChanged)));

            public static readonly DependencyProperty ImageSourceProperty =
               DependencyProperty.Register("ImageSource", typeof(ImageSource), typeof(CircularGaugeControl), null);

            public static readonly DependencyProperty ImageOffsetProperty =
               DependencyProperty.Register("ImageOffset", typeof(double), typeof(CircularGaugeControl), null);

            public static readonly DependencyProperty RangeIndicatorLightOffsetProperty =
               DependencyProperty.Register("RangeIndicatorLightOffset", typeof(double), typeof(CircularGaugeControl), null);

            public static readonly DependencyProperty ImageSizeProperty =
              DependencyProperty.Register("ImageSize", typeof(Size), typeof(CircularGaugeControl), null);

            public static readonly DependencyProperty RangeIndicatorRadiusProperty =
               DependencyProperty.Register("RangeIndicatorRadius", typeof(double), typeof(CircularGaugeControl), null);

            public static readonly DependencyProperty RangeIndicatorThicknessProperty =
              DependencyProperty.Register("RangeIndicatorThickness", typeof(double), typeof(CircularGaugeControl), null);

            public static readonly DependencyProperty ScaleLabelRadiusProperty =
                  DependencyProperty.Register("ScaleLabelRadius", typeof(double), typeof(CircularGaugeControl), null);

            public static readonly DependencyProperty ScaleLabelSizeProperty =
              DependencyProperty.Register("ScaleLabelSize", typeof(Size), typeof(CircularGaugeControl), null);

            public static readonly DependencyProperty ScaleLabelFontSizeProperty =
                 DependencyProperty.Register("ScaleLabelFontSize", typeof(double), typeof(CircularGaugeControl), null);

            public static readonly DependencyProperty ScaleLabelForegroundProperty =
                 DependencyProperty.Register("ScaleLabelForeground", typeof(Color), typeof(CircularGaugeControl), null);

            public static readonly DependencyProperty MajorTickSizeProperty =
                DependencyProperty.Register("MajorTickRectSize", typeof(Size), typeof(CircularGaugeControl), null);

            public static readonly DependencyProperty MinorTickSizeProperty =
               DependencyProperty.Register("MinorTickSize", typeof(Size), typeof(CircularGaugeControl), null);

            public static readonly DependencyProperty MajorTickColorProperty =
                DependencyProperty.Register("MajorTickColor", typeof(Color), typeof(CircularGaugeControl), null);

            public static readonly DependencyProperty MinorTickColorProperty =
              DependencyProperty.Register("MinorTickColor", typeof(Color), typeof(CircularGaugeControl), null);

            public static readonly DependencyProperty GaugeBackgroundColorProperty =
               DependencyProperty.Register("GaugeBackgroundColor", typeof(Color), typeof(CircularGaugeControl), null);

            public static readonly DependencyProperty PointerThicknessProperty =
            DependencyProperty.Register("PointerThickness", typeof(double), typeof(CircularGaugeControl), null);

            public static readonly DependencyProperty ResetPointerOnStartUpProperty =
            DependencyProperty.Register("ResetPointerOnStartUp", typeof(bool), typeof(CircularGaugeControl), new PropertyMetadata(false, null));

            public static readonly DependencyProperty ScaleValuePrecisionProperty =
            DependencyProperty.Register("ScaleValuePrecision", typeof(int), typeof(CircularGaugeControl), null);

            public static readonly DependencyProperty BelowOptimalRangeColorProperty =
                DependencyProperty.Register("BelowOptimalRangeColor", typeof(Color), typeof(CircularGaugeControl), null);

            public static readonly DependencyProperty OptimalRangeColorProperty =
                DependencyProperty.Register("OptimalRangeColor", typeof(Color), typeof(CircularGaugeControl), null);

            public static readonly DependencyProperty AboveOptimalRangeColorProperty =
                DependencyProperty.Register("AboveOptimalRangeColor", typeof(Color), typeof(CircularGaugeControl), null);

            public static readonly DependencyProperty DialTextProperty =
                DependencyProperty.Register("DialText", typeof(string), typeof(CircularGaugeControl), null);

            public static readonly DependencyProperty DialTextColorProperty =
                DependencyProperty.Register("DialTextColor", typeof(Color), typeof(CircularGaugeControl), null);

            public static readonly DependencyProperty DialTextFontSizeProperty =
                DependencyProperty.Register("DialTextFontSize", typeof(int), typeof(CircularGaugeControl), null);

            public static readonly DependencyProperty DialTextOffsetProperty =
                DependencyProperty.Register("DialTextOffset", typeof(double), typeof(CircularGaugeControl), null);

            public static readonly DependencyProperty RangeIndicatorLightRadiusProperty =
                DependencyProperty.Register("RangeIndicatorLightRadius", typeof(double), typeof(CircularGaugeControl), null);

            #endregion

            #region 封装属性

            /// <summary>
            /// 获取或设置当前属性的值
            /// </summary>
            public double CurrentValue
            {
                get { return (double)GetValue(CurrentValueProperty); }
                set { SetValue(CurrentValueProperty, value); }
            }

            /// <summary>
            /// 获取或设置刻度值的最小值
            /// </summary>
            public double MinValue
            {
                get { return (double)GetValue(MinValueProperty); }
                set { SetValue(MinValueProperty, value); }
            }

            /// <summary>
            /// 获取或设置刻度值的最大值
            /// </summary>
            public double MaxValue
            {
                get { return (double)GetValue(MaxValueProperty); }
                set { SetValue(MaxValueProperty, value); }
            }

            /// <summary>
            /// 获取或设置控件的半径
            /// </summary>
            public double Radius
            {
                get { return (double)GetValue(RadiusProperty); }
                set { SetValue(RadiusProperty, value); }
            }

            /// <summary>
            /// 获取或设置中心圆的半径
            /// </summary>
            public double PointerCapRadius
            {
                get { return (double)GetValue(PointerCapRadiusProperty); }
                set { SetValue(PointerCapRadiusProperty, value); }
            }

            /// <summary>
            /// 获取或设置指针长度
            /// </summary>
            public double PointerLength
            {
                get { return (double)GetValue(PointerLengthProperty); }
                set { SetValue(PointerLengthProperty, value); }
            }

            /// <summary>
            /// 获取或设置指针的粗度
            /// </summary>
            public double PointerThickness
            {
                get { return (double)GetValue(PointerThicknessProperty); }
                set { SetValue(PointerThicknessProperty, value); }
            }

            /// <summary>
            /// 获取或设置刻度的半径
            /// </summary>
            public double ScaleRadius
            {
                get { return (double)GetValue(ScaleRadiusProperty); }
                set { SetValue(ScaleRadiusProperty, value); }
            }

            /// <summary>
            /// 获取或设置刻度的起始角度
            /// </summary>
            public double ScaleStartAngle
            {
                get { return (double)GetValue(ScaleStartAngleProperty); }
                set { SetValue(ScaleStartAngleProperty, value); }
            }

            /// <summary>
            /// 获取或设置刻度的摆动角度
            /// </summary>
            public double ScaleSweepAngle
            {
                get { return (double)GetValue(ScaleSweepAngleProperty); }
                set { SetValue(ScaleSweepAngleProperty, value); }
            }

            /// <summary>
            /// 获取或设置主要分布点个数
            /// </summary>
            public double MajorDivisionsCount
            {
                get { return (double)GetValue(MajorDivisionsCountProperty); }
                set { SetValue(MajorDivisionsCountProperty, value); }
            }

            /// <summary>
            /// 获取或设置辅助分布点个数
            /// </summary>
            public double MinorDivisionsCount
            {
                get { return (double)GetValue(MinorDivisionsCountProperty); }
                set { SetValue(MinorDivisionsCountProperty, value); }
            }

            /// <summary>
            /// 获取或设置控件的最佳
            /// </summary>
            public double OptimalRangeEndValue
            {
                get { return (double)GetValue(OptimalRangeEndValueProperty); }
                set { SetValue(OptimalRangeEndValueProperty, value); }
            }

            /// <summary>
            /// 获取或设置控件的最佳
            /// </summary>
            public double OptimalRangeStartValue
            {
                get { return (double)GetValue(OptimalRangeStartValueProperty); }
                set { SetValue(OptimalRangeStartValueProperty, value); }
            }

            /// <summary>
            /// /// <summary>
            /// 获取或设置图片地址
            /// </summary>
            /// </summary>
            public ImageSource ImageSource
            {
                get { return (ImageSource)GetValue(ImageSourceProperty); }
                set { SetValue(ImageSourceProperty, value); }
            }

            /// <summary>
            /// /// <summary>
            /// 获取或设置控件的相对于中心点的图片位置
            /// </summary>
            /// </summary>
            public double ImageOffset
            {
                get { return (double)GetValue(ImageOffsetProperty); }
                set { SetValue(ImageOffsetProperty, value); }
            }

            /// <summary>
            ///  获取或设置控件的相对于中心点的指示灯位置
            /// </summary>
            public double RangeIndicatorLightOffset
            {
                get { return (double)GetValue(RangeIndicatorLightOffsetProperty); }
                set { SetValue(RangeIndicatorLightOffsetProperty, value); }
            }

            /// <summary>
            ///  获取或设置图片的大小
            /// </summary>
            public Size ImageSize
            {
                get { return (Size)GetValue(ImageSizeProperty); }
                set { SetValue(ImageSizeProperty, value); }
            }

            /// <summary>
            /// 获取或设置指示范围的半径
            /// </summary>
            public double RangeIndicatorRadius
            {
                get { return (double)GetValue(RangeIndicatorRadiusProperty); }
                set { SetValue(RangeIndicatorRadiusProperty, value); }
            }

            /// <summary>
            /// 获取或设置指示范围的粗度
            /// </summary>
            public double RangeIndicatorThickness
            {
                get { return (double)GetValue(RangeIndicatorThicknessProperty); }
                set { SetValue(RangeIndicatorThicknessProperty, value); }
            }

            /// <summary>
            /// 获取或设置刻度文本半径
            /// </summary>
            public double ScaleLabelRadius
            {
                get { return (double)GetValue(ScaleLabelRadiusProperty); }
                set { SetValue(ScaleLabelRadiusProperty, value); }
            }

            /// <summary>
            /// 设置或获取仪表的刻度文本的大小
            /// </summary>
            public Size ScaleLabelSize
            {
                get { return (Size)GetValue(ScaleLabelSizeProperty); }
                set { SetValue(ScaleLabelSizeProperty, value); }
            }

            /// <summary>
            /// 设置或获取仪表的刻度文本的字体大小
            /// </summary>
            public double ScaleLabelFontSize
            {
                get { return (double)GetValue(ScaleLabelFontSizeProperty); }
                set { SetValue(ScaleLabelFontSizeProperty, value); }
            }

            /// <summary>
            /// 设置或获取仪表的刻度文本的前景色
            /// </summary>
            public Color ScaleLabelForeground
            {
                get { return (Color)GetValue(ScaleLabelForegroundProperty); }
                set { SetValue(ScaleLabelForegroundProperty, value); }
            }

            /// <summary>
            /// 设置或获取仪表的主要刻度线的的长度
            /// </summary>
            public Size MajorTickSize
            {
                get { return (Size)GetValue(MajorTickSizeProperty); }
                set { SetValue(MajorTickSizeProperty, value); }
            }

            /// <summary>
            /// 设置或获取仪表的辅助刻度线的长度
            /// </summary>
            public Size MinorTickSize
            {
                get { return (Size)GetValue(MinorTickSizeProperty); }
                set { SetValue(MinorTickSizeProperty, value); }
            }

            /// <summary>
            /// 设置或获取仪表的主要刻度线的颜色
            /// </summary>
            public Color MajorTickColor
            {
                get { return (Color)GetValue(MajorTickColorProperty); }
                set { SetValue(MajorTickColorProperty, value); }
            }

            /// <summary>
            /// 设置或获取仪表的辅助刻度线的颜色
            /// </summary>
            public Color MinorTickColor
            {
                get { return (Color)GetValue(MinorTickColorProperty); }
                set { SetValue(MinorTickColorProperty, value); }
            }

            /// <summary>
            /// 设置或获取仪表的背景颜色
            /// </summary>
            public Color GaugeBackgroundColor
            {
                get { return (Color)GetValue(GaugeBackgroundColorProperty); }
                set { SetValue(GaugeBackgroundColorProperty, value); }
            }

            /// <summary>
            /// 设置或获取指针的起始角度
            /// </summary>
            public bool ResetPointerOnStartUp
            {
                get { return (bool)GetValue(ResetPointerOnStartUpProperty); }
                set { SetValue(ResetPointerOnStartUpProperty, value); }
            }

            /// <summary>
            /// 设置或获取刻度的精度
            /// </summary>
            public int ScaleValuePrecision
            {
                get { return (int)GetValue(ScaleValuePrecisionProperty); }
                set { SetValue(ScaleValuePrecisionProperty, value); }
            }

            /// <summary>
            /// 设置或获取左部条的颜色
            /// </summary>
            public Color BelowOptimalRangeColor
            {
                get { return (Color)GetValue(BelowOptimalRangeColorProperty); }
                set { SetValue(BelowOptimalRangeColorProperty, value); }
            }

            /// <summary>
            /// 设置或获取顶部条的颜色
            /// </summary>
            public Color OptimalRangeColor
            {
                get { return (Color)GetValue(OptimalRangeColorProperty); }
                set { SetValue(OptimalRangeColorProperty, value); }
            }

            /// <summary>
            /// 获取或设置度右边颜色
            /// </summary>
            public Color AboveOptimalRangeColor
            {
                get { return (Color)GetValue(AboveOptimalRangeColorProperty); }
                set { SetValue(AboveOptimalRangeColorProperty, value); }
            }

            /// <summary>
            /// 获取或设置文本
            /// </summary>
            public string DialText
            {
                get { return (string)GetValue(DialTextProperty); }
                set { SetValue(DialTextProperty, value); }
            }

            /// <summary>
            /// 获取或设置文本颜色
            /// </summary>
            public Color DialTextColor
            {
                get { return (Color)GetValue(DialTextColorProperty); }
                set { SetValue(DialTextColorProperty, value); }
            }

            /// <summary>
            /// 获取或设置文本位置
            /// </summary>
            public double DialTextOffset
            {
                get { return (double)GetValue(DialTextOffsetProperty); }
                set { SetValue(DialTextOffsetProperty, value); }
            }

            /// <summary>
            /// 获取或设置文本字体的大小
            /// </summary>
            public int DialTextFontSize
            {
                get { return (int)GetValue(DialTextFontSizeProperty); }
                set { SetValue(DialTextFontSizeProperty, value); }
            }

            /// <summary>
            /// 获取或设置指示灯的半径
            /// </summary>
            public double RangeIndicatorLightRadius
            {
                get { return (double)GetValue(RangeIndicatorLightRadiusProperty); }
                set { SetValue(RangeIndicatorLightRadiusProperty, value); }
            }

            #endregion

            #region 构造函数

            public CircularGaugeControl()
            {
                DefaultStyleKey = typeof(CircularGaugeControl);
            }

            #endregion

            #region 私有方法

            private static void OnCurrentValuePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
            {
                CircularGaugeControl gauge = d as CircularGaugeControl;
                gauge.OnCurrentValueChanged(e);
            }

            private static void OnOptimalRangeEndValuePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
            {
                CircularGaugeControl gauge = d as CircularGaugeControl;
                if ((double)e.NewValue > gauge.MaxValue)
                {
                    gauge.OptimalRangeEndValue = gauge.MaxValue;
                }
            }

            private static void OnOptimalRangeStartValuePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
            {
                CircularGaugeControl gauge = d as CircularGaugeControl;
                if ((double)e.NewValue < gauge.MinValue)
                {
                    gauge.OptimalRangeStartValue = gauge.MinValue;
                }
            }

            public virtual void OnCurrentValueChanged(DependencyPropertyChangedEventArgs e)
            {
                double newValue = (double)e.NewValue;
                double oldValue = (double)e.OldValue;
                if (newValue > this.MaxValue)
                {
                    newValue = this.MaxValue;
                }
                else if (newValue < this.MinValue)
                {
                    newValue = this.MinValue;
                }

                if (oldValue > this.MaxValue)
                {
                    oldValue = this.MaxValue;
                }
                else if (oldValue < this.MinValue)
                {
                    oldValue = this.MinValue;
                }
                if (pointer != null)
                {
                    double db1 = 0;
                    Double oldcurr_realworldunit = 0;
                    Double newcurr_realworldunit = 0;
                    Double realworldunit = (ScaleSweepAngle / (MaxValue - MinValue));
                    //Resetting the old value to min value the very first time.
                    if (oldValue == 0 && !isInitialValueSet)
                    {
                        oldValue = MinValue;
                        isInitialValueSet = true;

                    }
                    if (oldValue < 0)
                    {
                        db1 = MinValue + Math.Abs(oldValue);
                        oldcurr_realworldunit = ((double)(Math.Abs(db1 * realworldunit)));
                    }
                    else
                    {
                        db1 = Math.Abs(MinValue) + oldValue;
                        oldcurr_realworldunit = ((double)(db1 * realworldunit));
                    }
                    if (newValue < 0)
                    {
                        db1 = MinValue + Math.Abs(newValue);
                        newcurr_realworldunit = ((double)(Math.Abs(db1 * realworldunit)));
                    }
                    else
                    {
                        db1 = Math.Abs(MinValue) + newValue;
                        newcurr_realworldunit = ((double)(db1 * realworldunit));
                    }

                    Double oldcurrentvalueAngle = (ScaleStartAngle + oldcurr_realworldunit);
                    Double newcurrentvalueAngle = (ScaleStartAngle + newcurr_realworldunit);

                    //Animate the pointer from the old value to the new value
                    AnimatePointer(oldcurrentvalueAngle, newcurrentvalueAngle);
                }
            }

            private void AnimatePointer(double oldcurrentvalueAngle, double newcurrentvalueAngle)
            {
                if (pointer != null)
                {
                    DoubleAnimation da = new DoubleAnimation();
                    da.From = oldcurrentvalueAngle;
                    da.To = newcurrentvalueAngle;
                    double animDuration = Math.Abs(oldcurrentvalueAngle - newcurrentvalueAngle) * animatingSpeedFactor;
                    da.Duration = new Duration(TimeSpan.FromMilliseconds(animDuration));

                    Storyboard sb = new Storyboard();
                    sb.Completed += new EventHandler(sb_Completed);
                    sb.Children.Add(da);
                    Storyboard.SetTarget(da, pointer);
                    Storyboard.SetTargetProperty(da, new PropertyPath("(Path.RenderTransform).(TransformGroup.Children)[0].(RotateTransform.Angle)"));
                    if (newcurrentvalueAngle != oldcurrentvalueAngle)
                    {
                        sb.Begin();
                    }
                }
            }

            void MovePointer(double angleValue)
            {
                if (pointer != null)
                {
                    TransformGroup tg = pointer.RenderTransform as TransformGroup;
                    RotateTransform rt = tg.Children[0] as RotateTransform;
                    rt.Angle = angleValue;
                }
            }

            void sb_Completed(object sender, EventArgs e)
            {
                if (this.CurrentValue > OptimalRangeEndValue)
                {
                    lightIndicator.Fill = GetRangeIndicatorGradEffect(AboveOptimalRangeColor);

                }
                else if (this.CurrentValue <= OptimalRangeEndValue && this.CurrentValue >= OptimalRangeStartValue)
                {
                    lightIndicator.Fill = GetRangeIndicatorGradEffect(OptimalRangeColor);

                }
                else if (this.CurrentValue < OptimalRangeStartValue)
                {
                    lightIndicator.Fill = GetRangeIndicatorGradEffect(BelowOptimalRangeColor);
                }
            }

            private GradientBrush GetRangeIndicatorGradEffect(Color gradientColor)
            {

                LinearGradientBrush gradient = new LinearGradientBrush();
                gradient.StartPoint = new Point(0, 0);
                gradient.EndPoint = new Point(1, 1);
                GradientStop color1 = new GradientStop();
                if (gradientColor == Colors.Transparent)
                {
                    color1.Color = gradientColor;
                }
                else
                    color1.Color = Colors.LightGray;

                color1.Offset = 0.2;
                gradient.GradientStops.Add(color1);
                GradientStop color2 = new GradientStop();
                color2.Color = gradientColor; color2.Offset = 0.5;
                gradient.GradientStops.Add(color2);
                GradientStop color3 = new GradientStop();
                color3.Color = gradientColor; color3.Offset = 0.8;
                gradient.GradientStops.Add(color3);
                return gradient;
            }

     

            public override void OnApplyTemplate()
            {
                base.OnApplyTemplate();
                //Get reference to known elements on the control template
                rootGrid = GetTemplateChild("LayoutRoot") as Grid;
                pointer = GetTemplateChild("Pointer") as Path;
                pointerCap = GetTemplateChild("PointerCap") as Ellipse;
                lightIndicator = GetTemplateChild("RangeIndicatorLight") as Ellipse;

                //Draw scale and range indicator
                DrawScale();
                DrawRangeIndicator();

                //Set Zindex of pointer and pointer cap to a really high number so that it stays on top of the
                //scale and the range indicator
                Canvas.SetZIndex(pointer, 100000);
                Canvas.SetZIndex(pointerCap, 100001);

                if (ResetPointerOnStartUp)
                {
                    //Reset Pointer
                    MovePointer(ScaleStartAngle);
                }

     

            }


            //Drawing the scale with the Scale Radius
            private void DrawScale()
            {
                //Calculate one major tick angle
                Double majorTickUnitAngle = ScaleSweepAngle / MajorDivisionsCount;

                //Obtaining One minor tick angle
                Double minorTickUnitAngle = ScaleSweepAngle / MinorDivisionsCount;

                //Obtaining One major ticks value
                Double majorTicksUnitValue = (MaxValue - MinValue) / MajorDivisionsCount;
                majorTicksUnitValue = Math.Round(majorTicksUnitValue, ScaleValuePrecision);

                Double minvalue = MinValue; ;

                // Drawing Major scale ticks
                for (Double i = ScaleStartAngle; i <= (ScaleStartAngle + ScaleSweepAngle); i = i + majorTickUnitAngle)
                {

                    //Majortick is drawn as a rectangle
                    Rectangle majortickrect = new Rectangle();
                    majortickrect.Height = MajorTickSize.Height;
                    majortickrect.Width = MajorTickSize.Width;
                    majortickrect.Fill = new SolidColorBrush(MajorTickColor);
                    Point p = new Point(0.5, 0.5);
                    majortickrect.RenderTransformOrigin = p;
                    majortickrect.HorizontalAlignment = HorizontalAlignment.Center;
                    majortickrect.VerticalAlignment = VerticalAlignment.Center;

                    TransformGroup majortickgp = new TransformGroup();
                    RotateTransform majortickrt = new RotateTransform();

                    //Obtaining the angle in radians for calulating the points
                    Double i_radian = (i * Math.PI) / 180;
                    majortickrt.Angle = i;
                    majortickgp.Children.Add(majortickrt);
                    TranslateTransform majorticktt = new TranslateTransform();

                    //Finding the point on the Scale where the major ticks are drawn
                    //here drawing the points with center as (0,0)
                    majorticktt.X = (int)((ScaleRadius) * Math.Cos(i_radian));
                    majorticktt.Y = (int)((ScaleRadius) * Math.Sin(i_radian));

                    //Points for the textblock which hold the scale value
                    TranslateTransform majorscalevaluett = new TranslateTransform();
                    //here drawing the points with center as (0,0)
                    majorscalevaluett.X = (int)((ScaleLabelRadius) * Math.Cos(i_radian));
                    majorscalevaluett.Y = (int)((ScaleLabelRadius) * Math.Sin(i_radian));

                    //Defining the properties of the scale value textbox
                    TextBlock tb = new TextBlock();

                    tb.Height = ScaleLabelSize.Height;
                    tb.Width = ScaleLabelSize.Width;
                    tb.FontSize = ScaleLabelFontSize;
                    tb.Foreground = new SolidColorBrush(ScaleLabelForeground);
                    tb.TextAlignment = TextAlignment.Center;
                    tb.VerticalAlignment = VerticalAlignment.Center;
                    tb.HorizontalAlignment = HorizontalAlignment.Center;

                    //Writing and appending the scale value

                    //checking minvalue < maxvalue w.r.t scale precion value
                    if (Math.Round(minvalue, ScaleValuePrecision) <= Math.Round(MaxValue, ScaleValuePrecision))
                    {
                        minvalue = Math.Round(minvalue, ScaleValuePrecision);
                        tb.Text = minvalue.ToString();
                        minvalue = minvalue + majorTicksUnitValue;

                    }
                    else
                    {
                        break;
                    }
                    majortickgp.Children.Add(majorticktt);
                    majortickrect.RenderTransform = majortickgp;
                    tb.RenderTransform = majorscalevaluett;
                    rootGrid.Children.Add(majortickrect);
                    rootGrid.Children.Add(tb);


                    //Drawing the minor axis ticks
                    Double onedegree = ((i + majorTickUnitAngle) - i) / (MinorDivisionsCount);

                    if ((i < (ScaleStartAngle + ScaleSweepAngle)) && (Math.Round(minvalue, ScaleValuePrecision) <= Math.Round(MaxValue, ScaleValuePrecision)))
                    {
                        //Drawing the minor scale
                        for (Double mi = i + onedegree; mi < (i + majorTickUnitAngle); mi = mi + onedegree)
                        {
                            //here the minortick is drawn as a rectangle
                            Rectangle mr = new Rectangle();
                            mr.Height = MinorTickSize.Height;
                            mr.Width = MinorTickSize.Width;
                            mr.Fill = new SolidColorBrush(MinorTickColor);
                            mr.HorizontalAlignment = HorizontalAlignment.Center;
                            mr.VerticalAlignment = VerticalAlignment.Center;
                            Point p1 = new Point(0.5, 0.5);
                            mr.RenderTransformOrigin = p1;

                            TransformGroup minortickgp = new TransformGroup();
                            RotateTransform minortickrt = new RotateTransform();
                            minortickrt.Angle = mi;
                            minortickgp.Children.Add(minortickrt);
                            TranslateTransform minorticktt = new TranslateTransform();

                            //Obtaining the angle in radians for calulating the points
                            Double mi_radian = (mi * Math.PI) / 180;
                            //Finding the point on the Scale where the minor ticks are drawn
                            minorticktt.X = (int)((ScaleRadius) * Math.Cos(mi_radian));
                            minorticktt.Y = (int)((ScaleRadius) * Math.Sin(mi_radian));

                            minortickgp.Children.Add(minorticktt);
                            mr.RenderTransform = minortickgp;
                            rootGrid.Children.Add(mr);


                        }

                    }

                }
            }

            /// <summary>
            /// Obtaining the Point (x,y) in the circumference
            /// </summary>
            /// <param name="angle"></param>
            /// <param name="radius"></param>
            /// <returns></returns>
            private Point GetCircumferencePoint(Double angle, Double radius)
            {
                Double angle_radian = (angle * Math.PI) / 180;
                //Radius-- is the Radius of the gauge
                Double X = (Double)((Radius) + (radius) * Math.Cos(angle_radian));
                Double Y = (Double)((Radius) + (radius) * Math.Sin(angle_radian));
                Point p = new Point(X, Y);
                return p;
            }

            /// <summary>
            /// Draw the range indicator
            /// </summary>
            private void DrawRangeIndicator()
            {
                Double realworldunit = (ScaleSweepAngle / (MaxValue - MinValue));
                Double optimalStartAngle;
                Double optimalEndAngle;
                double db;

                //Checking whether the  OptimalRangeStartvalue is -ve
                if (OptimalRangeStartValue < 0)
                {
                    db = MinValue + Math.Abs(OptimalRangeStartValue);
                    optimalStartAngle = ((double)(Math.Abs(db * realworldunit)));
                }
                else
                {
                    db = Math.Abs(MinValue) + OptimalRangeStartValue;
                    optimalStartAngle = ((double)(db * realworldunit));
                }

                //Checking whether the  OptimalRangeEndvalue is -ve
                if (OptimalRangeEndValue < 0)
                {
                    db = MinValue + Math.Abs(OptimalRangeEndValue);
                    optimalEndAngle = ((double)(Math.Abs(db * realworldunit)));
                }
                else
                {
                    db = Math.Abs(MinValue) + OptimalRangeEndValue;
                    optimalEndAngle = ((double)(db * realworldunit));
                }
                // calculating the angle for optimal Start value

                Double optimalStartAngleFromStart = (ScaleStartAngle + optimalStartAngle);

                // calculating the angle for optimal Start value
                Double optimalEndAngleFromStart = (ScaleStartAngle + optimalEndAngle);

                //Calculating the Radius of the two arc for segment
                arcradius1 = (RangeIndicatorRadius + RangeIndicatorThickness);
                arcradius2 = RangeIndicatorRadius;

                double endAngle = ScaleStartAngle + ScaleSweepAngle;
                // Calculating the Points for the below Optimal Range segment from the center of the gauge

                Point A = GetCircumferencePoint(ScaleStartAngle, arcradius1);
                Point B = GetCircumferencePoint(ScaleStartAngle, arcradius2);
                Point C = GetCircumferencePoint(optimalStartAngleFromStart, arcradius2);
                Point D = GetCircumferencePoint(optimalStartAngleFromStart, arcradius1);

                bool isReflexAngle = Math.Abs(optimalStartAngleFromStart - ScaleStartAngle) > 180.0;
                DrawSegment(A, B, C, D, isReflexAngle, BelowOptimalRangeColor);             // Calculating the Points for the Optimal Range segment from the center of the gauge

                Point A1 = GetCircumferencePoint(optimalStartAngleFromStart, arcradius1);
                Point B1 = GetCircumferencePoint(optimalStartAngleFromStart, arcradius2);
                Point C1 = GetCircumferencePoint(optimalEndAngleFromStart, arcradius2);
                Point D1 = GetCircumferencePoint(optimalEndAngleFromStart, arcradius1);
                bool isReflexAngle1 = Math.Abs(optimalEndAngleFromStart - optimalStartAngleFromStart) > 180.0;
                DrawSegment(A1, B1, C1, D1, isReflexAngle1, OptimalRangeColor);

                // Calculating the Points for the Above Optimal Range segment from the center of the gauge

                Point A2 = GetCircumferencePoint(optimalEndAngleFromStart, arcradius1);
                Point B2 = GetCircumferencePoint(optimalEndAngleFromStart, arcradius2);
                Point C2 = GetCircumferencePoint(endAngle, arcradius2);
                Point D2 = GetCircumferencePoint(endAngle, arcradius1);
                bool isReflexAngle2 = Math.Abs(endAngle - optimalEndAngleFromStart) > 180.0;
                DrawSegment(A2, B2, C2, D2, isReflexAngle2, AboveOptimalRangeColor);
            }

            //Drawing the segment with two arc and two line

            private void DrawSegment(Point p1, Point p2, Point p3, Point p4, bool reflexangle, Color clr)
            {

                // Segment Geometry
                PathSegmentCollection segments = new PathSegmentCollection();

                // First line segment from pt p1 - pt p2
                segments.Add(new LineSegment() { Point = p2 });

                //Arc drawn from pt p2 - pt p3 with the RangeIndicatorRadius
                segments.Add(new ArcSegment()
                {
                    Size = new Size(arcradius2, arcradius2),
                    Point = p3,
                    SweepDirection = SweepDirection.Clockwise,
                    IsLargeArc = reflexangle

                });

                // Second line segment from pt p3 - pt p4
                segments.Add(new LineSegment() { Point = p4 });

                //Arc drawn from pt p4 - pt p1 with the Radius of arcradius1
                segments.Add(new ArcSegment()
                {
                    Size = new Size(arcradius1, arcradius1),
                    Point = p1,
                    SweepDirection = SweepDirection.Counterclockwise,
                    IsLargeArc = reflexangle

                });

                // Defining the segment path properties
                Color rangestrokecolor;
                if (clr == Colors.Transparent)
                {
                    rangestrokecolor = clr;
                }
                else
                    rangestrokecolor = Colors.White;

     

                rangeIndicator = new Path()
                {
                    StrokeLineJoin = PenLineJoin.Round,
                    Stroke = new SolidColorBrush(rangestrokecolor),
                    //Color.FromArgb(0xFF, 0xF5, 0x9A, 0x86)
                    Fill = new SolidColorBrush(clr),
                    Opacity = 0.65,
                    StrokeThickness = 0.25,
                    Data = new PathGeometry()
                    {
                        Figures = new PathFigureCollection()
                          {
                             new PathFigure()
                             {
                                 IsClosed = true,
                                 StartPoint = p1,
                                 Segments = segments
                             }
                         }
                    }
                };

                //Set Z index of range indicator
                rangeIndicator.SetValue(Canvas.ZIndexProperty, 150);
                // Adding the segment to the root grid
                rootGrid.Children.Add(rangeIndicator);

            }

            #endregion
        }
    }

     

    步骤2.定义一系列需要绑定的类用于将值转换器与绑定关联

    using System;
    using System.Net;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Documents;
    using System.Windows.Ink;
    using System.Windows.Input;
    using System.Windows.Media;
    using System.Windows.Media.Animation;
    using System.Windows.Shapes;
    using System.Windows.Data;
    using System.Globalization;

    namespace CircularGauge
    {
        #region 自定义绑定

        #region ColorToSolidColorBrushConverter

        public class ColorToSolidColorBrushConverter : IValueConverter
        {
            public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
            {
                Color c = (Color)value;
                return new SolidColorBrush(c);
            }

            public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
            {
                throw new NotImplementedException();
            }
        }

        #endregion

        #region ImageOffsetConverter

        public class ImageOffsetConverter : IValueConverter
        {
            public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
            {
                double d = (double)value;
                TranslateTransform tt = new TranslateTransform();
                tt.Y = d;
                return tt;
            }

            public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
            {
                throw new NotImplementedException();
            }
        }

        #endregion

        #region RadiusToDiameterConverter

        public class RadiusToDiameterConverter : IValueConverter
        {
            public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
            {
                double d = (double)value;
                return (d * 2);
            }

            public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
            {
                throw new NotImplementedException();
            }
        }
        #endregion

        #region PointerCenterConverter
        public class PointerCenterConverter : IValueConverter
        {

            #region IValueConverter 成员

            public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
            {
                double d = (double)value;
                TransformGroup tg = new TransformGroup();
                RotateTransform rt = new RotateTransform();
                TranslateTransform tt = new TranslateTransform();
                tt.X = d / 2;
                tg.Children.Add(rt);
                tg.Children.Add(tt);

                return tg;
            }

            public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
            {
                throw new NotImplementedException();
            }

            #endregion
        }

        #endregion

        #region ColorToSolidColorBrushConverter

        public class RangeIndicatorLightPositionConverter : IValueConverter
        {
            public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
            {
                double d = (double)value;
                TransformGroup tg = new TransformGroup();
                RotateTransform rt = new RotateTransform();
                TranslateTransform tt = new TranslateTransform();
                tt.Y = d;
                tg.Children.Add(rt);
                tg.Children.Add(tt);

                return tg;
            }

            public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
            {
                throw new NotImplementedException();
            }
        }

        #endregion

        #region SizeConverter
        public class SizeConverter : IValueConverter
        {
            public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
            {
                double d = 0;
                Size s = (Size)value;
                if (parameter.ToString() == "Height") d = s.Height;

                else if (parameter.ToString() == "Width") d = s.Width;

                return d;
            }

            public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
            {
                throw new NotImplementedException();
            }
        }
        #endregion

        #region GlassEffectWidthConverter
        public class GlassEffectWidthConverter : IValueConverter
        {
            public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
            {
                double dbl = (double)value;
                return (dbl * 2) * 0.94;
            }

            public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
            {
                throw new NotImplementedException();
            }
        }
        #endregion

        #region GlassEffectWidthConverter
        public class BackgroundColorConverter : IValueConverter
        {
            public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
            {
                Color c = (Color)value;
                RadialGradientBrush radBrush = new RadialGradientBrush();
                GradientStop g1 = new GradientStop();
                g1.Offset = 0.982;
                g1.Color = c;
                GradientStop g2 = new GradientStop();
                g2.Color = Color.FromArgb(0xFF, 0xAF, 0xB2, 0xB0);
                radBrush.GradientStops.Add(g1);
                radBrush.GradientStops.Add(g2);
                return radBrush;
            }

            public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
            {
                throw new NotImplementedException();
            }
        }
        #endregion

        #endregion
    }

     

    步骤3.generic.xaml用于定义样式

    <ResourceDictionary
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:CircularGauge">

        <local:ImageOffsetConverter x:Key="imageOffsetConverter" />
        <local:RadiusToDiameterConverter x:Key="radiusToDiameterConverter"/>
        <local:PointerCenterConverter x:Key="pointerCenterConverter"/>
        <local:RangeIndicatorLightPositionConverter x:Key="rangeIndicatorLightPositionConverter"/>
        <local:SizeConverter x:Key="sizeConverter" />
        <local:BackgroundColorConverter x:Key="backgroundColorConverter" />
        <local:ColorToSolidColorBrushConverter x:Key="colorToSolidColorBrushConverter" />
        <local:GlassEffectWidthConverter x:Key="glassEffectWidthConverter" />

        <Style TargetType="local:CircularGaugeControl" >
            <Setter Property="ResetPointerOnStartUp" Value="True" />
            <Setter Property="ScaleValuePrecision" Value="5" />
            <Setter Property="BorderBrush">
                <Setter.Value>
                    <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
                        <GradientStop Color="#FFA3AFD6" Offset="0.321"/>
                        <GradientStop Color="#FF8399A9" Offset="0.674"/>
                        <GradientStop Color="#FF718597" Offset="0.375"/>
                        <GradientStop Color="#FF617584" Offset="1"/>
                    </LinearGradientBrush>
                </Setter.Value>
            </Setter>
            <Setter Property="RangeIndicatorThickness" Value="5"/>
            <Setter Property="GaugeBackgroundColor" Value="Black" />
            <Setter Property="BelowOptimalRangeColor" Value="Yellow" />
            <Setter Property="OptimalRangeColor" Value="Green" />
            <Setter Property="AboveOptimalRangeColor" Value="Red" />
            <Setter Property="DialTextColor" Value="White" />
            <Setter Property="DialTextFontSize" Value="8" />
            <Setter Property="Template" >
                <Setter.Value>
                    <ControlTemplate TargetType="local:CircularGaugeControl">
                        <Grid x:Name="LayoutRoot" 
                              Height="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Radius,
                            Converter={StaticResource radiusToDiameterConverter}}"
                                Width="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Radius, Converter={StaticResource radiusToDiameterConverter}}" >
                            <Ellipse x:Name="OuterFrame"
                                     StrokeThickness="16"
                                     Height="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Radius, Converter={StaticResource radiusToDiameterConverter}}"
    Width="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Radius, Converter={StaticResource radiusToDiameterConverter}}"
    Fill="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=GaugeBackgroundColor, Converter={StaticResource backgroundColorConverter}}">
                                <Ellipse.Stroke>
                                    <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
                                        <GradientStop Color="#FF636060" Offset="1"/>
                                        <GradientStop Color="#FF5F5C5C" Offset="0"/>
                                        <GradientStop Color="#FFEEDEDE" Offset="0.35"/>
                                        <GradientStop Color="#FFA09595" Offset="0.705"/>
                                    </LinearGradientBrush>
                                </Ellipse.Stroke>
                            </Ellipse>

                            <Image Source="{TemplateBinding ImageSource}"
                                      Width="{Binding RelativeSource={RelativeSource TemplatedParent},
                                   Path=ImageSize, Converter={StaticResource sizeConverter}, ConverterParameter=Width }"
                                     
                                    Height="{Binding RelativeSource={RelativeSource TemplatedParent},
                                   Path=ImageSize, Converter={StaticResource sizeConverter}, ConverterParameter=Height }"
                                    
                                      RenderTransform="{Binding RelativeSource={RelativeSource TemplatedParent},
                                   Path=ImageOffset, Converter={StaticResource imageOffsetConverter}}">
                            </Image>
                            <TextBlock Text="{TemplateBinding DialText}" 
                                         HorizontalAlignment="Center" VerticalAlignment="Center" 
                                         Foreground="{Binding RelativeSource={RelativeSource TemplatedParent},
                                  Path=DialTextColor, Converter={StaticResource colorToSolidColorBrushConverter}}"
                                         FontSize="{TemplateBinding DialTextFontSize}"                            
                                         FontWeight="Bold"
                                                              
                                    RenderTransform="{Binding RelativeSource={RelativeSource TemplatedParent},
                                  Path=DialTextOffset, Converter={StaticResource rangeIndicatorLightPositionConverter}}">

                            </TextBlock>


                            <Path x:Name="Pointer" Stroke="#FFE91C1C" StrokeThickness="2"
                                   Width="{TemplateBinding PointerLength}"
                                    Height="{TemplateBinding PointerThickness}" HorizontalAlignment="Center"                           
                                  Data="M1,1 L1,10 L156,6 z" Stretch="Fill"  RenderTransformOrigin="0,0.5"
                                   RenderTransform="{Binding RelativeSource={RelativeSource TemplatedParent},
                                Path=PointerLength, Converter={StaticResource pointerCenterConverter}}">
                                <Path.Fill>
                                    <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
                                        <GradientStop Color="#FF890A0A" Offset="0.197"/>
                                        <GradientStop Color="#FFC40808" Offset="1"/>
                                        <GradientStop Color="#FFE32323" Offset="0.61"/>
                                    </LinearGradientBrush>
                                </Path.Fill>
                            </Path>
                            <Ellipse x:Name="PointerCap" Height="{TemplateBinding PointerCapRadius}" Width="{TemplateBinding PointerCapRadius}" StrokeThickness="4" Opacity="1" >

                                <Ellipse.Stroke>
                                    <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
                                        <GradientStop Color="#FF000000" Offset="0.675"/>
                                        <GradientStop Color="#FFC1B5B5" Offset="0.031"/>
                                    </LinearGradientBrush>
                                </Ellipse.Stroke>
                                <Ellipse.Fill>
                                    <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
                                        <GradientStop Color="#FF152029" Offset="0.846"/>
                                        <GradientStop Color="#FF140204" Offset="0.342"/>
                                    </LinearGradientBrush>
                                </Ellipse.Fill>
                            </Ellipse>
                            <!--Range indicator light-->
                            <Ellipse x:Name="RangeIndicatorLight"
                                      Height="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=RangeIndicatorLightRadius, Converter={StaticResource radiusToDiameterConverter}}"
                                        Width="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=RangeIndicatorLightRadius, Converter={StaticResource radiusToDiameterConverter}}"
                                    
                                     RenderTransform="{Binding RelativeSource={RelativeSource TemplatedParent},
                                      Path=RangeIndicatorLightOffset, Converter={StaticResource rangeIndicatorLightPositionConverter}}">

                                <!--Range indicator light off position effect-->

                                <Ellipse.Fill>

                                    <LinearGradientBrush StartPoint="0,0" EndPoint="1,1">

                                        <GradientStop Color="LightGray" Offset="0.2" />

                                        <GradientStop Color="Gray" Offset="0.5" />

                                        <GradientStop Color="Black" Offset="0.8" />

                                    </LinearGradientBrush>

                                </Ellipse.Fill>

                            </Ellipse>

                            <Path x:Name="GlassEffect" StrokeThickness="1" Stretch="Fill" VerticalAlignment="Bottom"
                                   Height="{TemplateBinding Radius}"
                                   Width="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Radius, Converter={StaticResource glassEffectWidthConverter}}"
                                   Opacity="0.18" Data="M265.99997,151.00005 C263.99994,194.00003 209.55908,259 135.00064,259 C60.442207,259 11,200.00003 5.9999995,157.00005 C5.0181994,148.55656 73.000877,112.00006 137.00053,112.00007 C199.00887,112.00008 266.72015,135.5164 265.99997,151.00005 z">
                                <Path.Fill>
                                    <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
                                        <GradientStop Color="#68FCFCFC"/>
                                        <GradientStop Color="#FFF8FCF8" Offset="1"/>
                                    </LinearGradientBrush>
                                </Path.Fill>
                            </Path>

                        </Grid>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </ResourceDictionary>

    二.演示

     

    1.Game.cs

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.ComponentModel;

    namespace SLGauge
    {
        public class Game : INotifyPropertyChanged
        {
            private double score;

            public double Score
            {
                get { return score; }
                set
                {
                    score = value;
                    if (PropertyChanged != null)
                    {
                        PropertyChanged(this, new PropertyChangedEventArgs("Score"));
                    }
                }
            }

            public Game(double d)
            {
                this.score = d;
            }

            public event PropertyChangedEventHandler PropertyChanged;
        }
    }

     

    2.MainPage.xaml

    <UserControl x:Class="SLGauge.MainPage"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
                 xmlns:controlsToolkit="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Toolkit" 
                   xmlns:gauge="clr-namespace:CircularGauge;assembly=CircularGauge"
                
        mc:Ignorable="d"
        d:DesignHeight="300" d:DesignWidth="650">

        <Grid x:Name="LayoutRoot">
            <Grid.Background>
                <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
                    <GradientStop Color="#FF552322" Offset="1"/>
                    <GradientStop Color="#FFFFFFFF" Offset="0"/>
                </LinearGradientBrush>
            </Grid.Background>
            <Grid >
                <Grid.RowDefinitions>
                    <RowDefinition Height="300" />
                    <RowDefinition Height="300"/>
                </Grid.RowDefinitions>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="330" />
                    <ColumnDefinition Width="330"/>
                    <ColumnDefinition Width="330" />
                    <ColumnDefinition Width="330"/>
                </Grid.ColumnDefinitions>
                <gauge:CircularGaugeControl x:Name="myGauge1"  Grid.Row="0"  Grid.Column="0"
                                              Radius="150"
                                              ScaleRadius="110"
                                              ScaleStartAngle="120"
                                              ScaleSweepAngle="300"
                                              PointerLength="85"
                                              PointerCapRadius="35"
                                              MinValue="0"
                                              MaxValue="1000"
                                              MajorDivisionsCount="10"
                                              MinorDivisionsCount="5"
                                              CurrentValue="{Binding Score}"
                                              ImageSource="2.png"
                                              ImageSize="60,50"
                                              RangeIndicatorThickness="8"
                                              RangeIndicatorRadius="120"
                                              RangeIndicatorLightRadius="10"
                                              RangeIndicatorLightOffset="80"
                                              ScaleLabelRadius="90"
                                              ScaleLabelSize="40,20"
                                              ScaleLabelFontSize="10"
                                              ScaleLabelForeground="LightGray"
                                              MajorTickSize="10,3"
                                              MinorTickSize="3,1"
                                              MajorTickColor="LightGray"
                                              MinorTickColor="LightGray"
                                              ImageOffset="-50"
                                              GaugeBackgroundColor="Black"
                                              PointerThickness ="16"
                                              OptimalRangeStartValue="300"
                                              OptimalRangeEndValue="700"
                                              DialTextOffset="40"
                                              DialText="Black"
                                              DialTextColor="AliceBlue"
                                            ></gauge:CircularGaugeControl>

                <gauge:CircularGaugeControl x:Name="myGauge2"  Grid.Column="1" Grid.Row="0"
                                              Radius="150"
                                              ScaleRadius="100"
                                              ScaleStartAngle="140"
                                              ScaleSweepAngle="270"
                                              PointerLength="90"
                                              PointerCapRadius="35"
                                              MinValue="0"
                                              MaxValue="1"
                                              MajorDivisionsCount="10"
                                              MinorDivisionsCount="5"
                                              OptimalRangeEndValue="0.8"
                                              OptimalRangeStartValue="0.5"
                                              CurrentValue="{Binding Score}"
                                              ImageSource="2.png"
                                              ImageSize="60,50"
                                              RangeIndicatorThickness="9"
                                              RangeIndicatorRadius="80"
                                              RangeIndicatorLightRadius="10"
                                          
                                              RangeIndicatorLightOffset="80"
                                              ScaleLabelRadius="115"
                                              ScaleLabelSize="40,20"
                                              ScaleLabelFontSize="10"
                                              ScaleLabelForeground="White"
                                              MajorTickSize="10,3"
                                              MinorTickSize="3,1"
                                              MajorTickColor="White"
                                              MinorTickColor="LightGray"
                                              ImageOffset="-50"
                                              GaugeBackgroundColor="CornflowerBlue"
                                              PointerThickness ="5"
                                              DialTextOffset="40"
                                              DialText="Black"
                                              DialTextColor="DarkBlue"
                                            >
                   
                </gauge:CircularGaugeControl>

                <gauge:CircularGaugeControl  x:Name="myGauge3" Grid.Column="0" Grid.Row="1"

                                              Radius="150"
                                              ScaleRadius="90"
                                              ScaleStartAngle="120"
                                              ScaleSweepAngle="300"
                                              PointerLength="80"
                                              PointerCapRadius="45"
                                              MinValue="-50"
                                              MaxValue="50"
                                              MajorDivisionsCount="10"
                                              MinorDivisionsCount="5"
           OptimalRangeStartValue="-10"
                                              OptimalRangeEndValue="30"
                                              CurrentValue="{Binding Score}"
                                              ImageSource="2.png"
                                              ImageSize="60,50"
                                              RangeIndicatorThickness="0"
                                              RangeIndicatorRadius="0"
                                              RangeIndicatorLightRadius="10"
                                              RangeIndicatorLightOffset="80"
                                              ScaleLabelRadius="110"
                                              ScaleLabelSize="40,20"
                                              ScaleLabelFontSize="10"
                                              ScaleLabelForeground="White"
                                              MajorTickSize="13,3"
                                              MinorTickSize="5,1"
                                              MajorTickColor="White"
                                              MinorTickColor="LightGray"
                                              ImageOffset="-50"
                                              GaugeBackgroundColor="DarkRed"
                                              PointerThickness ="20"                                          DialTextOffset="40"
                                              DialText="Dark Red"
                                              DialTextColor="DarkRed"
                                            ></gauge:CircularGaugeControl>

                <gauge:CircularGaugeControl x:Name="myGauge4" Grid.Column="1" Grid.Row="1"
                                              Radius="150"
                                              ScaleRadius="110"
                                              ScaleStartAngle="120"
                                              ResetPointerOnStartUp="True"
                                              ScaleSweepAngle="300"
                                              PointerLength="85"
                                              PointerCapRadius="35"
                                              MinValue="0"
                                              MaxValue="1000"
                                              MajorDivisionsCount="10"
                                              MinorDivisionsCount="5"
                                              CurrentValue="{Binding Score}"
                                              ImageSource="2.png"
                                              ImageSize="60,50"
                                              RangeIndicatorThickness="0"
                                              RangeIndicatorRadius="0"
                                              ScaleLabelRadius="90"
                                              ScaleLabelSize="40,20"
                                              ScaleLabelFontSize="11"
                                              ScaleLabelForeground="Black"
                                              MajorTickSize="10,3"
                                              MinorTickSize="3,1"
                                              MajorTickColor="DarkGray"
                                              MinorTickColor="DarkGray"
                                              ImageOffset="-50"
                                              GaugeBackgroundColor="DarkSlateGray"
                                              PointerThickness ="12"
                                              OptimalRangeStartValue="300"
                                              OptimalRangeEndValue="700"
                                              DialTextOffset="40"
                                              DialText="Dark SlateGray"
                                              DialTextColor="DarkSlateGray"
                                            ></gauge:CircularGaugeControl>
            </Grid>
        </Grid>
    </UserControl>

     

    4.MainPage.cs

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Net;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Documents;
    using System.Windows.Input;
    using System.Windows.Media;
    using System.Windows.Media.Animation;
    using System.Windows.Shapes;
    using System.Windows.Threading;

    namespace SLGauge
    {
        public partial class MainPage : UserControl
        {
            DispatcherTimer timer;
            Game game1;
            Game game2;
            Game game3;
            Game game4;

            public MainPage()
            {
                InitializeComponent();
                this.Loaded += new RoutedEventHandler(MainPage_Loaded);
            }

            void MainPage_Loaded(object sender, RoutedEventArgs e)
            {
                game1 = new Game(0);
                this.myGauge1.DataContext = game1;

                game2 = new Game(0);
                this.myGauge2.DataContext = game2;

                game3 = new Game(0);
                this.myGauge3.DataContext = game3;

                game4 = new Game(0);
                this.myGauge4.DataContext = game4;

                timer = new DispatcherTimer();
                timer.Interval = TimeSpan.FromMilliseconds(2500);
                timer.Tick += new EventHandler(timer_Tick);
                timer.Start();
            }

            void timer_Tick(object sender, EventArgs e)
            {
                Random r = new Random();
                game1.Score = r.Next(0, 1000);
                double val = r.Next(1, 10);
                game2.Score = val / 10;
                game3.Score = r.Next(-50, 50);
                game4.Score = r.Next(0, 1000);
            }
        }
    }

     

    注:源代码下载地址:https://files.cnblogs.com/salam/CircularGauge.rar

  • 相关阅读:
    redis之哨兵配置-2
    redis之主从配置-1
    MYSQL 在当前时间加上或减去一个时间段
    【转】将long数字序列化为json时,转换为字符串
    从技术到产品,从职场到创业,我这7年的痕迹
    可能是东半球第二好用的软件工具全部在这里(update in 2020.10.09)
    Java架构师面试题答案2020备忘录
    学习方法
    《精力管理》管理精力,而非时间
    《道德经》全文
  • 原文地址:https://www.cnblogs.com/salam/p/1784045.html
Copyright © 2011-2022 走看看