先看一下显示效果:
需要注意的地方有以下几点:
- 表盘的刻度分部,长刻度和短刻度显示。
- 在数值80W时,需要更改刻度盘的颜色渐变。
- 在数值80W时,更改库容总数背景的显示,也是颜色渐变。刻度盘控件属性定义:
刻度盘的定义:
using Microsoft.Expression.Shapes;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Animation;namespace Dashboard_Demo
{/// <summary>/// 刻度盘控件/// </summary>/// <remarks>add by zhidanfeng 2017.2.19</remarks>[TemplatePart(Name = "PART_IncreaseCircle", Type = typeof(Arc))]public class Dashboard : Control{private Arc PART_IncreaseCircle;/// <summary>/// 保存角度变化前的角度值(用于动画)/// </summary>private double OldAngle;#region Constructorsstatic Dashboard(){DefaultStyleKeyProperty.OverrideMetadata(typeof(Dashboard), new FrameworkPropertyMetadata(typeof(Dashboard)));}#endregion#region 依赖属性#region Angle 刻度盘起始角度/// <summary>/// 刻度盘起始角度依赖属性/// </summary>public static readonly DependencyProperty StartAngleProperty =DependencyProperty.Register("StartAngle",typeof(double),typeof(Dashboard),new PropertyMetadata(0d));/// <summary>/// 刻度盘起始角度/// </summary>public double StartAngle{get { return (double)GetValue(StartAngleProperty); }set { SetValue(StartAngleProperty, value); }}#endregion#region Angle 刻度盘结束角度依赖属性/// <summary>/// 刻度盘结束角度依赖属性/// </summary>public static readonly DependencyProperty EndAngleProperty =DependencyProperty.Register("EndAngle",typeof(double),typeof(Dashboard),new PropertyMetadata(0d));/// <summary>/// 刻度盘结束角度依赖属性/// </summary>public double EndAngle{get { return (double)GetValue(EndAngleProperty); }set{SetValue(EndAngleProperty, value);}}#endregion#region Minimum 最小值/// <summary>/// 最小值依赖属性,用于Binding/// </summary>public static readonly DependencyProperty MinimumProperty =DependencyProperty.Register("Minimum",typeof(double),typeof(Dashboard),new PropertyMetadata(0.0));/// <summary>/// 获取或设置最小值./// </summary>/// <value>最小值.</value>public double Minimum{get { return (double)GetValue(MinimumProperty); }set { SetValue(MinimumProperty, value); }}#endregion#region Maximum 最大值/// <summary>/// 最大值依赖属性,用于Binding/// </summary>public static readonly DependencyProperty MaximumProperty =DependencyProperty.Register("Maximum",typeof(double),typeof(Dashboard),new PropertyMetadata(100.0));/// <summary>/// 获取或设置最大值./// </summary>/// <value>最大值.</value>public double Maximum{get { return (double)GetValue(MaximumProperty); }set { SetValue(MaximumProperty, value); }}#endregion#region Value 当前值/// <summary>/// 最大值依赖属性,用于Binding/// </summary>public static readonly DependencyProperty ValueProperty =DependencyProperty.Register("Value",typeof(double),typeof(Dashboard),new PropertyMetadata(0.0, new PropertyChangedCallback(OnValuePropertyChanged)));/// <summary>/// 获取或设置当前值/// </summary>public double Value{get{if (PART_IncreaseCircle == null)return (double)GetValue(ValueProperty);if ((double)GetValue(ValueProperty) > 800000d){LinearGradientBrush brush = new LinearGradientBrush();brush.StartPoint = new Point(0, 0);brush.EndPoint = new Point(1, 0);brush.GradientStops.Add(new GradientStop((Color)ColorConverter.ConvertFromString("#f0b046"), 0));brush.GradientStops.Add(new GradientStop((Color)ColorConverter.ConvertFromString("#e83a2d"), 1));PART_IncreaseCircle.Stroke = brush;}else{LinearGradientBrush brush = new LinearGradientBrush();brush.StartPoint = new Point(0, 0);brush.EndPoint = new Point(1, 0);brush.GradientStops.Add(new GradientStop((Color)ColorConverter.ConvertFromString("#1ccabd"), 0));brush.GradientStops.Add(new GradientStop((Color)ColorConverter.ConvertFromString("#458ffc"), 1));PART_IncreaseCircle.Stroke = brush;}return (double)GetValue(ValueProperty);}set { SetValue(ValueProperty, value); }}private static void OnValuePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e){Dashboard dashboard = d as Dashboard;dashboard.OldAngle = dashboard.Angle;dashboard.SetAngle();dashboard.TransformAngle();}#endregion#region LongTickCount 长刻度个数public static readonly DependencyProperty LongTickCountProperty =DependencyProperty.Register("LongTickCount",typeof(int),typeof(Dashboard),new PropertyMetadata(5));/// <summary>/// 获取或设置长刻度个数,用于设置刻度盘显示几个长刻度/// </summary>public int LongTickCount{get { return (int)GetValue(LongTickCountProperty); }set { SetValue(LongTickCountProperty, value); }}#endregion#region ShortTickCount 短刻度个数public static readonly DependencyProperty ShortTickCountProperty =DependencyProperty.Register("ShortTickCount",typeof(int),typeof(Dashboard),new PropertyMetadata(3));/// <summary>/// 获取或设置两个长刻度之间的短刻度的个数/// </summary>public int ShortTickCount{get { return (int)GetValue(ShortTickCountProperty); }set { SetValue(ShortTickCountProperty, value); }}#endregion#region TickDurtion 刻度改变时的动画显示时长public static readonly DependencyProperty TickDurtionProperty = DependencyProperty.Register("TickDurtion", typeof(Duration), typeof(Dashboard),new PropertyMetadata(new Duration(TimeSpan.FromMilliseconds(400))));/// <summary>/// 刻度改变时的动画显示时长/// </summary>public Duration TickDurtion{get { return (Duration)GetValue(TickDurtionProperty); }set { SetValue(TickDurtionProperty, value); }}#endregion#region ShortTicksBrush 短刻度颜色public static readonly DependencyProperty ShortTicksBrushProperty = DependencyProperty.Register("ShortTicksBrush", typeof(Brush), typeof(Dashboard));/// <summary>/// 短刻度颜色/// </summary>public Brush ShortTicksBrush{get { return (Brush)GetValue(ShortTicksBrushProperty); }set { SetValue(ShortTicksBrushProperty, value); }}#endregion#region LongTicksBrush 长刻度颜色public static readonly DependencyProperty LongTicksBrushProperty = DependencyProperty.Register("LongTicksBrush", typeof(Brush), typeof(Dashboard));/// <summary>/// 长刻度颜色/// </summary>public Brush LongTicksBrush{get { return (Brush)GetValue(LongTicksBrushProperty); }set { SetValue(LongTicksBrushProperty, value); }}#endregion#region Contentpublic object Content{get { return (object)GetValue(ContentProperty); }set { SetValue(ContentProperty, value); }}public static readonly DependencyProperty ContentProperty =DependencyProperty.Register("Content", typeof(object), typeof(Dashboard));#endregion#region ContentTemplatepublic DataTemplate ContentTemplate{get { return (DataTemplate)GetValue(ContentTemplateProperty); }set { SetValue(ContentTemplateProperty, value); }}public static readonly DependencyProperty ContentTemplateProperty =DependencyProperty.Register("ContentTemplate", typeof(DataTemplate), typeof(Dashboard));#endregion#endregion#region Private依赖属性#region Angle 刻度盘当前值所对应的角度/// <summary>/// 刻度盘当前值所对应的角度依赖属性/// </summary>public static readonly DependencyProperty AngleProperty =DependencyProperty.Register("Angle",typeof(double),typeof(Dashboard),new PropertyMetadata(0d));/// <summary>/// 刻度盘当前值所对应的角度/// </summary>public double Angle{get { return (double)GetValue(AngleProperty); }private set { SetValue(AngleProperty, value); }}#endregion#region ShortTicks 短刻度线集合/// <summary>/// 短刻度线依赖属性,用于Binding/// </summary>public static readonly DependencyProperty ShortTicksProperty =DependencyProperty.Register("ShortTicks",typeof(IList<object>),typeof(Dashboard),new PropertyMetadata(null));/// <summary>/// 获取或设置短刻度线,用于绑定PathListBox的ItemsSource/// </summary>/// <value>短刻度线.</value>public IList<object> ShortTicks{get { return (IList<object>)GetValue(ShortTicksProperty); }private set { SetValue(ShortTicksProperty, value); }}#endregion#region LongTicks 长刻度线集合/// <summary>/// 长刻度线依赖属性,用于Binding/// </summary>public static readonly DependencyProperty LongTicksProperty =DependencyProperty.Register("LongTicks",typeof(IList<object>),typeof(Dashboard),new PropertyMetadata(null));/// <summary>/// 获取或设置长刻度线,用于绑定PathListBox的ItemsSource/// </summary>/// <value>长刻度线.</value>public IList<object> LongTicks{get { return (IList<object>)GetValue(LongTicksProperty); }private set { SetValue(LongTicksProperty, value); }}#endregion#region LongTicks 长刻度线上显示的数字/// <summary>/// 长刻度线依赖属性,用于Binding/// </summary>public static readonly DependencyProperty NumberListProperty =DependencyProperty.Register("NumberList",typeof(IList<object>),typeof(Dashboard),new PropertyMetadata(null));/// <summary>/// 获取或设置长刻度线,用于绑定PathListBox的ItemsSource/// </summary>/// <value>长刻度线.</value>public IList<object> NumberList{get { return (IList<object>)GetValue(NumberListProperty); }private set { SetValue(NumberListProperty, value); }}#endregion#endregion#region 重载public override void OnApplyTemplate(){base.OnApplyTemplate();this.PART_IncreaseCircle = GetTemplateChild("PART_IncreaseCircle") as Arc;this.SetTicks();this.SetAngle();this.TransformAngle();}#endregion#region Private方法/// <summary>/// 设置刻度线/// </summary>private void SetTicks(){List<object> numbers = new List<object>();List<object> shortticks = new List<object>();List<object> longticks = new List<object>();for (int i = 0; i < this.LongTickCount; i++){numbers.Add(Math.Round(this.Minimum + (this.Maximum - this.Minimum) / (this.LongTickCount - 1) * i));longticks.Add(new object());}for (int i = 0; i < (this.LongTickCount - 1) * (this.ShortTickCount + 1) + 1; i++){shortticks.Add(new object());}this.ShortTicks = shortticks;this.LongTicks = longticks;this.NumberList = numbers;}/// <summary>/// 根据当前值设置圆弧的EndAngle/// </summary>private void SetAngle(){if (this.Value < this.Minimum){this.Angle = this.StartAngle;return;}if (this.Value > this.Maximum){this.Angle = this.EndAngle;return;}var diff = this.Maximum - this.Minimum;var valueDiff = this.Value - this.Minimum;this.Angle = this.StartAngle + (this.EndAngle - this.StartAngle) / diff * valueDiff;}/// <summary>/// 角度值变化动画/// </summary>private void TransformAngle(){if (this.PART_IncreaseCircle != null){DoubleAnimation doubleAnimation = new DoubleAnimation(this.OldAngle, this.Angle, this.TickDurtion);this.PART_IncreaseCircle.BeginAnimation(Arc.EndAngleProperty, doubleAnimation);}}#endregion}
}
设置刻度盘的style:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:ed="http://schemas.microsoft.com/expression/2010/drawing"xmlns:ec="http://schemas.microsoft.com/expression/2010/controls" xmlns:local ="clr-namespace:Dashboard_Demo"><!-- 流量盘 --><ControlTemplate x:Key="Flow" TargetType="{x:Type local:Dashboard}"><Grid><!-- 刻度盘完整圆弧 --><ed:Arc x:Name="DoubleCircle" Margin="50" ArcThickness="3" ArcThicknessUnit="Pixel"EndAngle="{TemplateBinding EndAngle}"SnapsToDevicePixels="True"StartAngle="{TemplateBinding StartAngle}"Stretch="None" Stroke="#002266" Opacity="0.16" StrokeThickness="3" UseLayoutRounding="True" /><!-- 刻度盘当前值对应的圆弧 --><ed:Arc x:Name="PART_IncreaseCircle" Margin="50" ArcThickness="3" ArcThicknessUnit="Pixel"RenderTransformOrigin="0.5,0.5"StartAngle="{TemplateBinding StartAngle}"Stretch="None" StrokeThickness="3" /><!-- 短刻度 --><ec:PathListBox x:Name="ShoartTick" IsHitTestVisible="False"ItemsSource="{TemplateBinding ShortTicks}"><ec:PathListBox.ItemTemplate><DataTemplate><Border Width="1" Height="8"Background="{Binding ShortTicksBrush,RelativeSource={RelativeSource AncestorType={x:Type local:Dashboard}}}"SnapsToDevicePixels="True" UseLayoutRounding="False" /></DataTemplate></ec:PathListBox.ItemTemplate><ec:PathListBox.LayoutPaths><ec:LayoutPath Distribution="Even" Orientation="OrientToPath"SourceElement="{Binding ElementName=ShortTickPath}" /></ec:PathListBox.LayoutPaths></ec:PathListBox><!-- 长刻度 --><ec:PathListBox x:Name="LongTick" IsHitTestVisible="False"ItemsSource="{TemplateBinding LongTicks}"><ec:PathListBox.ItemTemplate><DataTemplate><Border Width="1" Height="13"Background="{Binding LongTicksBrush,RelativeSource={RelativeSource AncestorType={x:Type local:Dashboard}}}"SnapsToDevicePixels="True" UseLayoutRounding="False" /></DataTemplate></ec:PathListBox.ItemTemplate><ec:PathListBox.LayoutPaths><ec:LayoutPath Distribution="Even" Orientation="OrientToPath"SourceElement="{Binding ElementName=LongTickPath}" /></ec:PathListBox.LayoutPaths></ec:PathListBox><!-- 刻度上显示的数字 --><ec:PathListBox x:Name="Number" IsHitTestVisible="False"ItemsSource="{TemplateBinding NumberList}"><ec:PathListBox.ItemTemplate><DataTemplate><TextBlock RenderTransformOrigin="0.5,0.5" Text="{Binding}"></TextBlock></DataTemplate></ec:PathListBox.ItemTemplate><ec:PathListBox.LayoutPaths><ec:LayoutPath Distribution="Even" Orientation="OrientToPath"SourceElement="{Binding ElementName=NumberPath}" /></ec:PathListBox.LayoutPaths></ec:PathListBox><!--#region 路径--><ed:Arc x:Name="ShortTickPath" Margin="30" ArcThickness="0" ArcThicknessUnit="Pixel"EndAngle="{TemplateBinding EndAngle}"StartAngle="{TemplateBinding StartAngle}"Stretch="None" /><ed:Arc x:Name="LongTickPath" Margin="25" ArcThickness="0" ArcThicknessUnit="Pixel"EndAngle="{TemplateBinding EndAngle}"StartAngle="{TemplateBinding StartAngle}"Stretch="None" /><ed:Arc x:Name="NumberPath" Margin="10" ArcThickness="0" ArcThicknessUnit="Pixel"EndAngle="{TemplateBinding EndAngle}"StartAngle="{TemplateBinding StartAngle}"Stretch="None" /><!--#endregion--><ContentPresenter Content="{TemplateBinding Content}" ContentTemplate="{TemplateBinding ContentTemplate}" /></Grid></ControlTemplate><DataTemplate x:Key="DefaultLabelPanel" x:Shared="False"><Border Width="70" Margin="0,0,0,20" HorizontalAlignment="Center" VerticalAlignment="Bottom"BorderBrush="#00A0FB" BorderThickness="1" CornerRadius="3" Padding="0,2"SnapsToDevicePixels="True" UseLayoutRounding="True"><TextBlock HorizontalAlignment="Center" VerticalAlignment="Center" FontFamily="Agency FB" Foreground="White"Text="{Binding Path=Value,StringFormat={}{0:N1}KW,RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type local:Dashboard}}}" /></Border></DataTemplate><Style TargetType="{x:Type local:Dashboard}"><Setter Property="StartAngle" Value="-140" /><Setter Property="EndAngle" Value="140" /><Setter Property="Foreground" Value="#929093" /><Setter Property="BorderBrush" Value="#746E7A" /><Setter Property="ShortTicksBrush" Value="#746E7A" /><Setter Property="LongTicksBrush" Value="#746E7A" /><Setter Property="Template" Value="{StaticResource Flow}" /><Setter Property="ContentTemplate" Value="{StaticResource DefaultLabelPanel}" /></Style><LinearGradientBrush x:Key="LargeArcCenterBackground" StartPoint="0.0,0" EndPoint="0,1"><GradientStop Color="#d84a36" Offset="0"/><GradientStop Color="#ec7c6c" Offset="1"/></LinearGradientBrush><LinearGradientBrush x:Key="LargeOutArcBackground" StartPoint="0.0,0" EndPoint="0,1"><GradientStop Color="#e2aaa3" Offset="0"/><GradientStop Color="#e4b4ac" Offset="1"/></LinearGradientBrush><LinearGradientBrush x:Key="LargeBorderArcBackground" StartPoint="0.0,0" EndPoint="0,1"><GradientStop Color="#e3d5d3" Offset="0"/><GradientStop Color="#e4d8d6" Offset="1"/></LinearGradientBrush><LinearGradientBrush x:Key="SmallArcCenterBackground" StartPoint="0.0,0" EndPoint="0,1"><GradientStop Color="#389cfa" Offset="0"/><GradientStop Color="#73b8fd" Offset="1"/></LinearGradientBrush><LinearGradientBrush x:Key="SmallOutArcBackground" StartPoint="0.0,0" EndPoint="0,1" Opacity="0.4"><GradientStop Color="#389AfC" Offset="0"/><GradientStop Color="#78AEFC" Offset="1"/></LinearGradientBrush><LinearGradientBrush x:Key="SmallBorderArcBackground" StartPoint="0.0,0" EndPoint="0,1" Opacity="0.1"><GradientStop Color="#389AfC" Offset="0"/><GradientStop Color="#78AEFC" Offset="1"/></LinearGradientBrush>
</ResourceDictionary>
- 库容总数背景渐变实现:
-
<DataTemplate x:Key="Small"><Grid Margin="0,110,0,0" HorizontalAlignment="Center"><Ellipse x:Name="ellipse1" Width="166" Height="166" Fill="{StaticResource SmallBorderArcBackground}" Margin="0 -43" VerticalAlignment="Top"/><Ellipse x:Name="ellipse2" Width="147" Height="147" Fill="{StaticResource SmallOutArcBackground}" Margin="0 -33" VerticalAlignment="Top"/><Ellipse x:Name="ellipse3" Width="133" Height="133" Fill="{StaticResource SmallArcCenterBackground}" Margin="0 -25" VerticalAlignment="Top"/><TextBlock Margin="0,30" HorizontalAlignment="Center" FontSize="24" Foreground="White" FontWeight="Bold"Text="{Binding Path=Value,StringFormat={}{0:N0},ElementName=dashboard1}" /></Grid></DataTemplate><DataTemplate x:Key="Large"><Grid Margin="0,110,0,0" HorizontalAlignment="Center"><Ellipse x:Name="ellipse1" Width="166" Height="166" Fill="{StaticResource LargeBorderArcBackground}" Margin="0 -43" VerticalAlignment="Top"/><Ellipse x:Name="ellipse2" Width="147" Height="147" Fill="{StaticResource LargeOutArcBackground}" Margin="0 -33" VerticalAlignment="Top"/><Ellipse x:Name="ellipse3" Width="133" Height="133" Fill="{StaticResource LargeArcCenterBackground}" Margin="0 -25" VerticalAlignment="Top"/><TextBlock Margin="0,30" HorizontalAlignment="Center" FontSize="24" Foreground="White" FontWeight="Bold"Text="{Binding Path=Value,StringFormat={}{0:N0},ElementName=dashboard1}" /></Grid></DataTemplate>
实现效果(低于80W):
-
高于80W时显示:
-
csdn下载地址:https://download.csdn.net/download/chulijun3107/88058570
-
github:GitHub - chulijun3107/Dashboard_Demo: WPF userControl