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

posted @ 2010-07-23 22:13  ForrestWoo  阅读(10298)  评论(5编辑  收藏  举报