使用Telerik控件库制作WPF项目中的折线图、柱状图、饼图和甜甜圈图
本博客是基于 .Net Framework 4.6.2 的WPF(MVVM)項目,Telerik版本為 2016.2.613.40。其他版本是否可用不詳。
本文章所使用數據均為測試數據,無任何意義。
本博客所有內容是根據本人實際使用情況,面向百度及Telerik官方文檔、Telerik官方示例、Telerik官方論壇。如超出需求,可自行查詢。
//引入命名空間 xmlns:telerik="http://schemas.telerik.com/2008/xaml/presentation" xmlns:chartView="clr-namespace:Telerik.Windows.Controls.ChartView;assembly=Telerik.Windows.Controls.Chart" xmlns:animation="clr-namespace:Management.Control"Palette可選樣式: https://docs.telerik.com/devtools/wpf/controls/radchartview/features/palettes/introduction
1.折線圖:
1.1 XAML
<telerik:RadCartesianChart Margin="10" <!--設置顏色主題-->Palette="Autumn" <!--折線圖上鼠標滑過提示的樣式-->TrackBallInfoStyle="{StaticResource trackBallInfoStyle}"> <telerik:RadCartesianChart.HorizontalAxis> <!-- 水平軸 --><chartView:CategoricalAxis Title="月份" <!-- 標題 -->GapLength="0.8" <!-- 間隔長度[0-1] -->PlotMode="OnTicksPadded" /></telerik:RadCartesianChart.HorizontalAxis><telerik:RadCartesianChart.VerticalAxis> <!-- 垂直軸 --><chartView:LinearAxis Title="案件數量"/></telerik:RadCartesianChart.VerticalAxis><telerik:RadCartesianChart.Grid> <!-- 控制顯示和Y軸垂直的陰影 --><telerik:CartesianChartGrid MajorLinesVisibility="Y" StripLinesVisibility="Y"/></telerik:RadCartesianChart.Grid><telerik:RadCartesianChart.Series><telerik:LineSeries ShowLabels="True" <!-- 顯示標簽,位于折線拐點處-->ValueBinding="Number" <!-- 水平軸綁定的模型-->CategoryBinding="KeyName" <!-- 垂直軸綁定的模型-->animation:ChartAnimationUtilities.CartesianAnimation="DropWithDelay" <!-- 數據加載動畫-->TrackBallInfoTemplate="{StaticResource consumptionTrackBallInfoTemplate}" <!-- 鼠標滑過折線圖的提示信息 -->ItemsSource="{Binding MonLawcases}"> <!--綁定的數據源 --></telerik:LineSeries></telerik:RadCartesianChart.Series><telerik:RadCartesianChart.Behaviors><telerik:ChartTrackBallBehavior ShowIntersectionPoints="True" /> <!-- 展示折線拐點 --><!--<telerik:ChartPanAndZoomBehavior PanMode="Horizontal" DragMode="Pan" ZoomMode="Horizontal"/>--> <!-- 顯示水平滾動條 --></telerik:RadCartesianChart.Behaviors></telerik:RadCartesianChart>1.2 資源樣式
折線圖上鼠標滑過提示的樣式
<Style x:Key="trackBallInfoStyle" TargetType="telerik:TrackBallInfoControl"><Setter Property="Template"><Setter.Value><ControlTemplate TargetType="telerik:TrackBallInfoControl"><Border Background="White" BorderBrush="#FF808080" BorderThickness="1"><StackPanel Name="panel" Grid.Row="1" Margin="3 0 3 0" /></Border></ControlTemplate></Setter.Value></Setter></Style>鼠標滑過數據展示模板
<DataTemplate x:Key="consumptionTrackBallInfoTemplate"><Border><StackPanel Orientation="Vertical"><StackPanel Orientation="Horizontal"><TextBlock Text="月份:" /><TextBlock Text="{Binding DataPoint.Category}" /></StackPanel><StackPanel Orientation="Horizontal"><TextBlock Text="數量:" /><TextBlock Text="{Binding DataPoint.Value}" /></StackPanel></StackPanel></Border></DataTemplate>1.3 數據模型
public class LineInfo {public int KeyName { get; set; }public int Number { get; set; } } private ObservableCollection<LineInfo> _monLawcases;public ObservableCollection<LineInfo> MonLawcases{get { return _monLawcases; }set { SetProperty(ref _monLawcases, value, "MonLawcases"); }}注:SetProperty(ref _monLawcases, value, “MonLawcases”);為Prism框架寫法,可替換為
public class XXX:INotifyPropertyChanged{private ObservableCollection<LineInfo> _monLawcases;public ObservableCollection<LineInfo> MonLawcases{public event PropertyChangedEventHandler PropertyChanged;get { return _monLawcases; }set { _monLawcases= value; if (PropertyChanged != null){ PropertyChanged(this, new PropertyChangedEventArgs("MonLawcases")); } }}}1.4 效果圖
2.柱狀圖:
2.1 XAML
<telerik:RadCartesianChart Grid.Column="2" Margin="10"HoverMode="FadeOtherSeries"TrackBallInfoStyle="{StaticResource trackBallInfoStyle}" ><telerik:RadCartesianChart.HorizontalAxis><telerik:CategoricalAxis Title="單位名稱" GapLength="0.5"/></telerik:RadCartesianChart.HorizontalAxis><telerik:RadCartesianChart.VerticalAxis><telerik:LinearAxis Title="檢材數量"SmartLabelsMode="SmartStepAndRange"/></telerik:RadCartesianChart.VerticalAxis><telerik:RadCartesianChart.Grid><telerik:CartesianChartGrid MajorLinesVisibility="Y" StripLinesVisibility="Y"/></telerik:RadCartesianChart.Grid><telerik:RadCartesianChart.Behaviors><telerik:ChartTrackBallBehavior /><!--<telerik:ChartPanAndZoomBehavior PanMode="Horizontal" DragMode="Pan" ZoomMode="Horizontal"/>--></telerik:RadCartesianChart.Behaviors><telerik:RadCartesianChart.Series><telerik:BarSeries ValueBinding="Count"ShowLabels="True" CategoryBinding="Name" animation:ChartAnimationUtilities.CartesianAnimation="RiseWithDelay" <!-- 加載動畫 -->TrackBallInfoTemplate="{StaticResource consumptionTrackBallInfoTemplate1}"ItemsSource="{Binding UnitDevices}"></telerik:BarSeries></telerik:RadCartesianChart.Series></telerik:RadCartesianChart>2.2 資源樣式
鼠標滑過提示的樣式
<Style x:Key="trackBallInfoStyle" TargetType="telerik:TrackBallInfoControl"><Setter Property="Template"><Setter.Value><ControlTemplate TargetType="telerik:TrackBallInfoControl"><Border Background="White" BorderBrush="#FF808080" BorderThickness="1"><StackPanel Name="panel" Grid.Row="1" Margin="3 0 3 0" /></Border></ControlTemplate></Setter.Value></Setter></Style>鼠標滑過數據展示模板
<DataTemplate x:Key="consumptionTrackBallInfoTemplate1"><Border><StackPanel Orientation="Vertical"><StackPanel Orientation="Horizontal"><TextBlock Text="單位:" /><TextBlock Text="{Binding DataPoint.Category}" /></StackPanel><StackPanel Orientation="Horizontal"><TextBlock Text="檢材數量:" /><TextBlock Text="{Binding DataPoint.Value}" /></StackPanel></StackPanel></Border></DataTemplate>2.3 數據模型
public class BarChartParam{public string Name { get; set; }public int Count { get; set; }} private ObservableCollection<BarChartParam> _unitDevices;public ObservableCollection<BarChartParam> UnitDevices{get { return _unitDevices; }set { SetProperty(ref _unitDevices, value, "UnitDevices"); }}2.4 效果圖
3 餅圖
3.1 XAML
<!-- 餅圖 --> <telerik:RadPieChart Palette="Summer" x:Name="PieChart"><!-- 控制餅圖標簽顯示位置 DisplayMode 可選--><telerik:RadPieChart.SmartLabelsStrategy > <telerik:PieChartSmartLabelsStrategy DisplayMode="SpiderUnaligned"/></telerik:RadPieChart.SmartLabelsStrategy><!-- 控制餅圖選擇模式 此處為當單選 DataPointSelectionMode可選--><telerik:RadPieChart.Behaviors><telerik:ChartSelectionBehavior DataPointSelectionMode="Single"/></telerik:RadPieChart.Behaviors><telerik:RadPieChart.Series><!--LabelFormat="##.##%" ShowLabels="True"--> <!-- 標簽顯示樣式, ##.##% : 例 7.00% --><telerik:PieSeries ItemsSource="{Binding UnitDevicePercentage}" animation:ChartAnimationUtilities.PieAnimation="Slice" <!-- 加載動畫,注意和折線圖的區別 --> ValueBinding="Number"> <!-- 餅圖數據綁定的模型 --><telerik:PieSeries.LegendSettings><telerik:DataPointLegendSettings TitleBinding="Name" /></telerik:PieSeries.LegendSettings><!-- 餅圖標簽和餅圖的連接線,此處使用默認的灰色 --><telerik:PieSeries.LabelConnectorsSettings><telerik:ChartSeriesLabelConnectorsSettings /></telerik:PieSeries.LabelConnectorsSettings><!-- 餅圖標簽顯示樣式 --><telerik:PieSeries.LabelDefinitions><telerik:ChartSeriesLabelDefinition Template="{StaticResource PieLableTemplate}"/></telerik:PieSeries.LabelDefinitions></telerik:PieSeries></telerik:RadPieChart.Series> </telerik:RadPieChart> <!--圖例--><telerik:RadLegend Grid.Column="1"HorizontalAlignment="Center"VerticalAlignment="Bottom"Items="{Binding LegendItems, ElementName=PieChart}"/>$標簽注意 若使用模板的寫法,數據源需要是計算好的百分比(如 4.45),若不想計算百分比可按 代碼中注釋的 LabelFormat寫法,控件庫可自行計算百分比,此時不能使用模板樣式 -------------甜甜圈圖同樣。
$圖例注意綁定的Items 為餅圖的名稱x:Name=“PieChart”。
3.2 資源樣式
//餅圖標簽顯示樣式模板<DataTemplate x:Key="PieLableTemplate"><StackPanel Orientation="Horizontal"><TextBlock Text="{Binding DataItem.Name}"/><TextBlock Text=":"/><TextBlock Text="{Binding DataItem.Number,StringFormat=\{0:n2\}}"/><TextBlock Text="%"/></StackPanel></DataTemplate>3.3 數據模型
public class PieInfo{public string Name { get; set; }public double Number { get; set; }} private ObservableCollection<PieInfo> _unitDevicePercentage;public ObservableCollection<PieInfo> UnitDevicePercentage{get { return _unitDevicePercentage; }set { SetProperty(ref _unitDevicePercentage, value, "UnitDevicePercentage"); }}3.4 效果圖
4 甜甜圈圖
4.1 XAML
telerik:RadPieChart Palette="Windows8" Grid.Row="0"Grid.Column="0"Grid.RowSpan="2"x:Name="DoughnutSeries"><telerik:RadPieChart.SmartLabelsStrategy><telerik:PieChartSmartLabelsStrategy DisplayMode="SpiderUnaligned"/></telerik:RadPieChart.SmartLabelsStrategy><telerik:RadPieChart.Series><telerik:DoughnutSeries ItemsSource="{Binding PhoneBrands}" InnerRadiusFactor="0.65" <!--內圓半徑-->animation:ChartAnimationUtilities.PieAnimation="RadiusFactor" ValueBinding="Number"><telerik:DoughnutSeries.LegendSettings><telerik:DataPointLegendSettings TitleBinding="Name" /></telerik:DoughnutSeries.LegendSettings><telerik:DoughnutSeries.LabelConnectorsSettings><telerik:ChartSeriesLabelConnectorsSettings /></telerik:DoughnutSeries.LabelConnectorsSettings><telerik:DoughnutSeries.LabelDefinitions><telerik:ChartSeriesLabelDefinition Template="{StaticResource PieLableTemplate}"/></telerik:DoughnutSeries.LabelDefinitions></telerik:DoughnutSeries></telerik:RadPieChart.Series></telerik:RadPieChart> <!-- 圖例 --> <telerik:RadLegend Grid.Column="1"Grid.Row="0"HorizontalAlignment="Center"VerticalAlignment="Bottom"Items="{Binding LegendItems, ElementName=DoughnutSeries}"/>4.2 資源樣式
標簽顯示模板
<DataTemplate x:Key="PieLableTemplate"><StackPanel Orientation="Horizontal"><TextBlock Text="{Binding DataItem.Name}"/><TextBlock Text=":"/><TextBlock Text="{Binding DataItem.Number,StringFormat=\{0:n2\}}"/><TextBlock Text="%"/></StackPanel></DataTemplate>4.3 數據模型
public class PieInfo{public string Name { get; set; }public double Number { get; set; }} private ObservableCollection<PieInfo> _phoneBrands;public ObservableCollection<PieInfo> PhoneBrands{get { return _phoneBrands; }set { SetProperty(ref _phoneBrands, value, "PhoneBrands"); }}4.4 效果圖
5 甜甜圈內嵌餅圖
5.1 XAML
<Grid Grid.Row="2"Grid.Column="2"><Grid.ColumnDefinitions><ColumnDefinition/><ColumnDefinition Width="auto"/></Grid.ColumnDefinitions><Grid.RowDefinitions><RowDefinition/><RowDefinition/></Grid.RowDefinitions><!--甜甜圈圖-外--><telerik:RadPieChart Palette="Windows8" Grid.Row="0"Grid.Column="0"Grid.RowSpan="2"x:Name="DoughnutSeries"><telerik:RadPieChart.SmartLabelsStrategy><telerik:PieChartSmartLabelsStrategy DisplayMode="SpiderUnaligned"/></telerik:RadPieChart.SmartLabelsStrategy><telerik:RadPieChart.Series><telerik:DoughnutSeries ItemsSource="{Binding PhoneBrands}" InnerRadiusFactor="0.65" animation:ChartAnimationUtilities.PieAnimation="RadiusFactor" ValueBinding="Number"><telerik:DoughnutSeries.LegendSettings><telerik:DataPointLegendSettings TitleBinding="Name" /></telerik:DoughnutSeries.LegendSettings><telerik:DoughnutSeries.LabelConnectorsSettings><telerik:ChartSeriesLabelConnectorsSettings /></telerik:DoughnutSeries.LabelConnectorsSettings><telerik:DoughnutSeries.LabelDefinitions><telerik:ChartSeriesLabelDefinition Template="{StaticResource PieLableTemplate}"/></telerik:DoughnutSeries.LabelDefinitions></telerik:DoughnutSeries></telerik:RadPieChart.Series></telerik:RadPieChart><!--外圖例--><telerik:RadLegend Grid.Column="1"Grid.Row="0"HorizontalAlignment="Center"VerticalAlignment="Bottom"Items="{Binding LegendItems, ElementName=DoughnutSeries}"/><!---甜甜圈-內--><telerik:RadPieChart Palette="Summer"x:Name="innerPieSeries"Grid.Row="0"Grid.Column="0"Grid.RowSpan="2"><telerik:RadPieChart.Behaviors><telerik:ChartSelectionBehavior DataPointSelectionMode="Single"SelectionChanged="ChartSelectionBehavior_SelectionChanged" /> <!-- 餅圖選擇改變事件,根據內餅圖選擇的塊,動態改變外甜甜圈的數據 --></telerik:RadPieChart.Behaviors><telerik:RadPieChart.Series><telerik:PieSeries RadiusFactor="0.55" Loaded="PieSeries_Loaded" <!-- 數據加載完畢觸發自動選中第一塊餅圖-->animation:ChartAnimationUtilities.PieAnimation="StartAngle" ItemsSource="{Binding UnitDevicePercentage}"ValueBinding="Number"><telerik:PieSeries.LegendSettings><telerik:DataPointLegendSettings TitleBinding="Name" /></telerik:PieSeries.LegendSettings><telerik:PieSeries.LabelConnectorsSettings><telerik:ChartSeriesLabelConnectorsSettings /></telerik:PieSeries.LabelConnectorsSettings><telerik:PieSeries.LabelDefinitions><telerik:ChartSeriesLabelDefinition Template="{StaticResource PieLableTemplate}"/></telerik:PieSeries.LabelDefinitions></telerik:PieSeries></telerik:RadPieChart.Series></telerik:RadPieChart><!--內圖例--><telerik:RadLegend Grid.Column="1"Grid.Row="1"HorizontalAlignment="Center"VerticalAlignment="Bottom"Items="{Binding LegendItems, ElementName=innerPieSeries}"/></Grid>5.2 資源樣式
<!--餅圖標簽模板--><DataTemplate x:Key="PieLableTemplate"><StackPanel Orientation="Horizontal"><TextBlock Text="{Binding DataItem.Name}"/><TextBlock Text=":"/><TextBlock Text="{Binding DataItem.Number,StringFormat=\{0:n2\}}"/><TextBlock Text="%"/></StackPanel></DataTemplate>5.3 數據模型
public class PieInfo {public string Name { get; set; }public double Number { get; set; } } private ObservableCollection<PieInfo> _phoneBrands; public ObservableCollection<PieInfo> PhoneBrands {get { return _phoneBrands; }set { SetProperty(ref _phoneBrands, value, "PhoneBrands"); } }private ObservableCollection<PieInfo> _unitDevicePercentage; public ObservableCollection<PieInfo> UnitDevicePercentage {get { return _unitDevicePercentage; }set { SetProperty(ref _unitDevicePercentage, value, "UnitDevicePercentage"); } }5.4 數據加載事件與餅圖點擊事件
餅圖選擇改變事件
private void ChartSelectionBehavior_SelectionChanged(object sender, ChartSelectionChangedEventArgs e){try{var selectedPoint = this.innerPieSeries.SelectedPoints;if (selectedPoint == null || selectedPoint.Count <= 0){_dataAnalysisPageViewModel.PhoneBrands.Clear();return;}if (!(selectedPoint[0].DataItem is PieInfo))return;var pieInfo = selectedPoint[0].DataItem as PieInfo;if (pieInfo == null)return;//此處根據 pieInfo.Name 查詢對應數據//數據查詢比較耗時,此處可另開線程Task.Run(() =>{//操作UI需通知到主線程// App.Current.Dispatcher.Invoke(() => { XXX });});}catch (Exception ex){LoggerService.Error($"ChartSelectionBehavior_SelectionChanged Error{ex.ToString()}", ex);}}餅圖數據加載完畢事件
private void PieSeries_Loaded(object sender, RoutedEventArgs e){try{var allSeries = this.innerPieSeries.Series;if (allSeries == null || allSeries.Count <= 0)return;var allDataPoints = allSeries.FirstOrDefault()?.DataPoints;if (allDataPoints == null || allDataPoints.Count <= 0)return;var dataPoints = allDataPoints.FirstOrDefault();if (dataPoints == null)return;// 默認選中第一個餅塊dataPoints.IsSelected = true;// 簡單粗暴的寫法//this.innerPieSeries.Series[0].DataPoints[0].IsSelected = true;}catch (Exception ex){LoggerService.Error($"PieSeries_Loaded Error{ex.ToString()}", ex);}}5.5 效果圖
6 數據加載動畫
動畫使用時引入相應的命名空間即可,使用方法如上例。
xmlns:animation="clr-namespace:Management.Control"代碼如下:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Media; using System.Windows.Media.Animation; using System.Windows.Shapes; using Telerik.Charting; using Telerik.Windows.Controls; using Telerik.Windows.Controls.ChartView;namespace Management.Control {//餅圖動畫枚舉public enum PieAnimation{None = 0,StartAngle = 1,SweepAngle = 2,RadiusFactor = 4,Slice = 8,}//折線圖、柱狀圖動畫枚舉public enum CartesianAnimation{None,Drop,//下降DropWithDelay,Rise,//上升RiseWithDelay,Stretch,StackedBars,}/// <summary>/// 圖表動畫/// </summary>public class ChartAnimationUtilities{public static readonly DependencyProperty CartesianAnimationProperty = DependencyProperty.RegisterAttached("CartesianAnimation",typeof(CartesianAnimation),typeof(ChartAnimationUtilities),new PropertyMetadata(CartesianAnimation.None, CartesianAnimationChanged));public static readonly DependencyProperty PieAnimationProperty = DependencyProperty.RegisterAttached("PieAnimation",typeof(PieAnimation),typeof(ChartAnimationUtilities),new PropertyMetadata(PieAnimation.None, PieAnimationChanged));private static readonly DependencyProperty RunningAnimationsCountProperty = DependencyProperty.RegisterAttached("RunningAnimationsCount",typeof(int),typeof(ChartAnimationUtilities),new PropertyMetadata(0));private static readonly DependencyProperty StartAngleProperty = DependencyProperty.RegisterAttached("StartAngle",typeof(double),typeof(ChartAnimationUtilities),new PropertyMetadata(double.NaN, StartAngleChanged));private static readonly DependencyProperty SweepAngleProperty = DependencyProperty.RegisterAttached("SweepAngle",typeof(double),typeof(ChartAnimationUtilities),new PropertyMetadata(double.NaN, SweepAngleChanged));private static readonly DependencyProperty SeriesScaleTransformXProperty = DependencyProperty.RegisterAttached("SeriesScaleTransformX",typeof(double),typeof(ChartAnimationUtilities),new PropertyMetadata(double.NaN, SeriesScaleTransformXChanged));private static readonly DependencyProperty SeriesScaleTransformYProperty = DependencyProperty.RegisterAttached("SeriesScaleTransformY",typeof(double),typeof(ChartAnimationUtilities),new PropertyMetadata(double.NaN, SeriesScaleTransformYChanged));private static readonly DependencyProperty BarScaleTransformXProperty = DependencyProperty.RegisterAttached("BarScaleTransformX",typeof(double),typeof(ChartAnimationUtilities),new PropertyMetadata(double.NaN, BarScaleTransformXChanged));private static readonly DependencyProperty BarScaleTransformYProperty = DependencyProperty.RegisterAttached("BarScaleTransformY",typeof(double),typeof(ChartAnimationUtilities),new PropertyMetadata(double.NaN, BarScaleTransformYChanged));private static readonly DependencyProperty SliceScaleTransformXYProperty = DependencyProperty.RegisterAttached("SliceScaleTransformXY",typeof(double),typeof(ChartAnimationUtilities),new PropertyMetadata(double.NaN, SliceScaleTransformXYChanged));private static readonly DependencyProperty SeriesTranslateTransformYProperty = DependencyProperty.RegisterAttached("SeriesTranslateTransformY",typeof(double),typeof(ChartAnimationUtilities),new PropertyMetadata(double.NaN, SeriesTranslateTransformYChanged));private const int DelayInMilliseconds = 1000;private const int PieDelayInMilliseconds = 300;private const int BarDelayInMilliseconds = 200;private static Duration AnimationDuration = new Duration(TimeSpan.FromMilliseconds(1500));private static Duration PieSliceAnimationDuration = new Duration(TimeSpan.FromMilliseconds(500));private static Duration BarAnimationDuration = new Duration(TimeSpan.FromMilliseconds(500));private static object locker = new object();public static CartesianAnimation GetCartesianAnimation(DependencyObject obj){return (CartesianAnimation)obj.GetValue(CartesianAnimationProperty);}public static void SetCartesianAnimation(DependencyObject obj, CartesianAnimation value){obj.SetValue(CartesianAnimationProperty, value);}public static PieAnimation GetPieAnimation(DependencyObject obj){return (PieAnimation)obj.GetValue(PieAnimationProperty);}public static void SetPieAnimation(DependencyObject obj, PieAnimation value){obj.SetValue(PieAnimationProperty, value);}public static void DispatchRunAnimations(RadChartBase chart){IEnumerable<ChartSeries> series = null;RadCartesianChart cartesianChart = chart as RadCartesianChart;if (cartesianChart != null){series = cartesianChart.Series;}RadPieChart pieChart = chart as RadPieChart;if (pieChart != null){series = pieChart.Series;}if (series.Any(s => (int)s.GetValue(ChartAnimationUtilities.RunningAnimationsCountProperty) > 0)){return;}foreach (ChartSeries s in series){DispatchRunSeriesAnimations(s);}}private static void CartesianAnimationChanged(DependencyObject target, DependencyPropertyChangedEventArgs args){CartesianSeries series = (CartesianSeries)target;if ((CartesianAnimation)args.NewValue == CartesianAnimation.None){series.Loaded -= ChartSeries_Loaded;series.DataBindingComplete -= ChartSeries_DataBindingComplete;}if ((CartesianAnimation)args.OldValue == CartesianAnimation.None){series.Loaded += ChartSeries_Loaded;series.DataBindingComplete += ChartSeries_DataBindingComplete;}}private static void PieAnimationChanged(DependencyObject target, DependencyPropertyChangedEventArgs args){PieSeries series = (PieSeries)target;if ((PieAnimation)args.NewValue == PieAnimation.None){series.Loaded -= ChartSeries_Loaded;series.DataBindingComplete -= ChartSeries_DataBindingComplete;}if ((PieAnimation)args.OldValue == PieAnimation.None){series.Loaded += ChartSeries_Loaded;series.DataBindingComplete += ChartSeries_DataBindingComplete;}}private static void StartAngleChanged(DependencyObject target, DependencyPropertyChangedEventArgs args){PieSeries series = (PieSeries)target;double startAngle = (double)args.NewValue;if (double.IsNaN(startAngle) || series.AngleRange.StartAngle == startAngle){return;}series.AngleRange = new AngleRange(startAngle, series.AngleRange.SweepAngle, series.AngleRange.SweepDirection);}private static void SweepAngleChanged(DependencyObject target, DependencyPropertyChangedEventArgs args){PieSeries series = (PieSeries)target;double sweepAngle = (double)args.NewValue;if (double.IsNaN(sweepAngle) || series.AngleRange.SweepAngle == sweepAngle){return;}series.AngleRange = new AngleRange(series.AngleRange.StartAngle, sweepAngle, series.AngleRange.SweepDirection);}private static void SeriesScaleTransformXChanged(DependencyObject target, DependencyPropertyChangedEventArgs args){ChartSeries series = (ChartSeries)target;double scaleX = (double)args.NewValue;if (!double.IsNaN(scaleX)){ScaleTransform transform = (ScaleTransform)series.RenderTransform;transform.ScaleX = scaleX;}}private static void SeriesScaleTransformYChanged(DependencyObject target, DependencyPropertyChangedEventArgs args){ChartSeries series = (ChartSeries)target;double scaleY = (double)args.NewValue;if (!double.IsNaN(scaleY)){ScaleTransform transform = (ScaleTransform)series.RenderTransform;transform.ScaleY = (double)args.NewValue;}}private static void BarScaleTransformXChanged(DependencyObject target, DependencyPropertyChangedEventArgs args){FrameworkElement bar = (FrameworkElement)target;double scaleX = (double)args.NewValue;if (!double.IsNaN(scaleX)){ScaleTransform transform = (ScaleTransform)bar.RenderTransform;transform.ScaleX = scaleX;}}private static void BarScaleTransformYChanged(DependencyObject target, DependencyPropertyChangedEventArgs args){FrameworkElement bar = (FrameworkElement)target;double scaleY = (double)args.NewValue;if (!double.IsNaN(scaleY)){ScaleTransform transform = (ScaleTransform)bar.RenderTransform;transform.ScaleY = (double)args.NewValue;}}private static void SliceScaleTransformXYChanged(DependencyObject target, DependencyPropertyChangedEventArgs args){Path slice = (Path)target;double scale = (double)args.NewValue;if (!double.IsNaN(scale)){ScaleTransform transform = (ScaleTransform)slice.RenderTransform;transform.ScaleX = scale;transform.ScaleY = scale;}}private static void SeriesTranslateTransformYChanged(DependencyObject target, DependencyPropertyChangedEventArgs args){ChartSeries series = (ChartSeries)target;double transformY = (double)args.NewValue;if (!double.IsNaN(transformY)){TranslateTransform transform = (TranslateTransform)series.RenderTransform;transform.Y = (double)args.NewValue;}}private static void ChartSeries_Loaded(object sender, RoutedEventArgs e){RunOrDispatchAnimations((ChartSeries)sender);}private static void ChartSeries_DataBindingComplete(object sender, EventArgs e){DispatchRunSeriesAnimations((ChartSeries)sender);}private static void RunOrDispatchAnimations(ChartSeries series){bool started = TryRunSeriesAnimation(series);if (!started){DispatchRunSeriesAnimations(series);}}private static void DispatchRunSeriesAnimations(ChartSeries series){series.Dispatcher.BeginInvoke((Action)(() => TryRunSeriesAnimation(series)));}private static bool HasDataPointsInPlotRange(ChartSeries series){IList<DataPoint> dataPoints = GetDataPoints(series);foreach (DataPoint dp in dataPoints){if (dp.IsInPlotRange){return true;}}return false;}private static bool TryRunSeriesAnimation(ChartSeries series){if (!HasDataPointsInPlotRange(series)){return false;}int count = (int)series.GetValue(ChartAnimationUtilities.RunningAnimationsCountProperty);if (count > 0){return false;}bool started = false;CartesianSeries cartesianSeries = series as CartesianSeries;if (cartesianSeries != null){CartesianAnimation animation = GetCartesianAnimation(cartesianSeries);if (animation == CartesianAnimation.Drop || animation == CartesianAnimation.DropWithDelay){bool useDelay = animation == CartesianAnimation.DropWithDelay;started |= TryRunDropAnimtation(cartesianSeries, useDelay);}if (animation == CartesianAnimation.Rise || animation == CartesianAnimation.RiseWithDelay){bool useDelay = animation == CartesianAnimation.RiseWithDelay;started |= TryRunRiseAnimtation(cartesianSeries, useDelay);}if (animation == CartesianAnimation.Stretch){started |= TryRunStretchAnimtation(cartesianSeries);}if (animation == CartesianAnimation.StackedBars){started |= TryRunStackedBarsAnimtation(cartesianSeries);}}PieSeries pieSeries = series as PieSeries;if (pieSeries != null){PieAnimation animation = GetPieAnimation(pieSeries);if (animation.HasFlag(PieAnimation.RadiusFactor)){started |= TryRunRadiusFactorAnimtation(pieSeries);}if (animation.HasFlag(PieAnimation.Slice)){started |= TryRunSliceAnimtation(pieSeries);}if (animation.HasFlag(PieAnimation.StartAngle)){started |= TryRunStartAngleAnimtation(pieSeries);}if (animation.HasFlag(PieAnimation.SweepAngle)){started |= TryRunSweepAngleAnimtation(pieSeries);}}return started;}private static bool TryRunRadiusFactorAnimtation(PieSeries series){DoubleAnimation animation = new DoubleAnimation();animation.From = 0.0;animation.Duration = AnimationDuration;animation.EasingFunction = new CubicEase() { EasingMode = EasingMode.EaseOut, };Storyboard.SetTargetProperty(animation, new PropertyPath(PieSeries.RadiusFactorProperty));Storyboard.SetTarget(animation, series);Storyboard storyboard = new Storyboard();storyboard.Children.Add(animation);return Run(storyboard, series);}private static bool TryRunSliceAnimtation(PieSeries series){Canvas renderSurface = Telerik.Windows.Controls.ChildrenOfTypeExtensions.FindChildByType<Canvas>(series);List<Path> slices = new List<Path>();foreach (UIElement uiElement in renderSurface.Children){Path slice = uiElement as Path;if (slice != null && slice.DataContext is PieDataPoint){slices.Add(slice);}}Storyboard storyboard = new Storyboard();Point center = new Point(series.Chart.ActualWidth / 2, series.Chart.ActualHeight / 2);TimeSpan? beginTime = null;for (int i = 0; i < slices.Count; i++){var animation = BuildSliceAnimation(slices[i], beginTime, PieSliceAnimationDuration, center, series);storyboard.Children.Add(animation);beginTime = new TimeSpan(0, 0, 0, 0, PieDelayInMilliseconds * (i + 1) / slices.Count);}bool showLabels = series.ShowLabels;series.ShowLabels = false;Action completed = () => series.ShowLabels = showLabels;bool started = Run(storyboard, series, completed);if (!started){completed();}return started;}private static bool TryRunStartAngleAnimtation(PieSeries series){double startAngle = (double)series.GetAnimationBaseValue(ChartAnimationUtilities.StartAngleProperty);if (double.IsNaN(startAngle)){series.SetValue(ChartAnimationUtilities.StartAngleProperty, series.AngleRange.StartAngle);startAngle = series.AngleRange.SweepAngle;}DoubleAnimation animation = new DoubleAnimation();animation.From = startAngle - 90;animation.To = startAngle;animation.Duration = AnimationDuration;animation.EasingFunction = new CubicEase() { EasingMode = EasingMode.EaseOut, };Storyboard.SetTargetProperty(animation, new PropertyPath(ChartAnimationUtilities.StartAngleProperty));Storyboard.SetTarget(animation, series);Storyboard storyboard = new Storyboard();storyboard.Children.Add(animation);return Run(storyboard, series);}private static bool TryRunSweepAngleAnimtation(PieSeries series){double sweepAngle = (double)series.GetAnimationBaseValue(ChartAnimationUtilities.SweepAngleProperty);if (double.IsNaN(sweepAngle)){series.SetValue(ChartAnimationUtilities.SweepAngleProperty, series.AngleRange.SweepAngle);sweepAngle = series.AngleRange.SweepAngle;}DoubleAnimation animation = new DoubleAnimation();animation.From = 0.0;animation.To = sweepAngle;animation.Duration = AnimationDuration;animation.EasingFunction = new CubicEase() { EasingMode = EasingMode.EaseOut, };Storyboard.SetTargetProperty(animation, new PropertyPath(ChartAnimationUtilities.SweepAngleProperty));Storyboard.SetTarget(animation, series);Storyboard storyboard = new Storyboard();storyboard.Children.Add(animation);return Run(storyboard, series);}private static bool TryRunRiseAnimtation(CartesianSeries series, bool useDelay){RadRect plotAreClip = series.Chart.PlotAreaClip;bool isHorizontalBar = !IsSeriesHorizontal(series);bool isInverse = IsNumericalAxisInverse(series);double centerX = 0;double centerY = 0;if (isHorizontalBar){centerX = isInverse ? plotAreClip.Right : plotAreClip.X;}else{centerY = isInverse ? plotAreClip.Y : plotAreClip.Bottom;}var scaleTransform = new ScaleTransform();scaleTransform.ScaleX = isHorizontalBar ? 0 : 1;scaleTransform.ScaleY = isHorizontalBar ? 1 : 0;scaleTransform.CenterX = centerX;scaleTransform.CenterY = centerY;series.RenderTransform = scaleTransform;TimeSpan? beginTime = useDelay ? CalculateBeginTime(series) : null;DoubleAnimation animation = new DoubleAnimation();animation.From = 0;animation.To = 1;animation.Duration = AnimationDuration;if (beginTime != null){animation.EasingFunction = new CubicEase() { EasingMode = EasingMode.EaseOut, };}else{animation.EasingFunction = new CircleEase() { EasingMode = EasingMode.EaseOut, };}Storyboard.SetTarget(animation, series);if (isHorizontalBar){Storyboard.SetTargetProperty(animation, new PropertyPath(ChartAnimationUtilities.SeriesScaleTransformXProperty));}else{Storyboard.SetTargetProperty(animation, new PropertyPath(ChartAnimationUtilities.SeriesScaleTransformYProperty));}Storyboard storyboard = new Storyboard();storyboard.Children.Add(animation);if (beginTime != null){storyboard.BeginTime = beginTime;}return Run(storyboard, series);}private static bool TryRunStretchAnimtation(CartesianSeries series){RadRect plotAreClip = series.Chart.PlotAreaClip;bool isHorizontal = IsSeriesHorizontal(series);ScaleTransform transform = new ScaleTransform();transform.ScaleX = isHorizontal ? 1 : 0;transform.ScaleY = isHorizontal ? 0 : 1;transform.CenterX = isHorizontal ? 0 : CalculateHorizontalSeriesMiddle(series);transform.CenterY = isHorizontal ? CalculateVerticalSeriesMiddle(series) : 0;if (!IsValidNumber(transform.CenterX) || !IsValidNumber(transform.CenterY)){return false;}series.RenderTransform = transform;DoubleAnimation animation = new DoubleAnimation();animation.From = 0;animation.To = 1;animation.Duration = AnimationDuration;animation.EasingFunction = new CircleEase() { EasingMode = EasingMode.EaseOut, };DependencyProperty prop = isHorizontal ? ChartAnimationUtilities.SeriesScaleTransformYProperty : ChartAnimationUtilities.SeriesScaleTransformXProperty;Storyboard.SetTargetProperty(animation, new PropertyPath(prop));Storyboard.SetTarget(animation, series);Storyboard storyboard = new Storyboard();storyboard.Children.Add(animation);return Run(storyboard, series);}private static bool TryRunDropAnimtation(CartesianSeries series, bool useDelay){IList<DataPoint> dataPoints = GetDataPoints(series);bool isInverse = IsNumericalAxisInverse(series);double offsetY = isInverse ? double.PositiveInfinity : double.NegativeInfinity;foreach (DataPoint dp in dataPoints){if (isInverse && dp.IsInPlotRange && dp.LayoutSlot.Center.Y < offsetY){offsetY = dp.LayoutSlot.Center.Y;}else if (!isInverse && dp.IsInPlotRange && offsetY < dp.LayoutSlot.Center.Y){offsetY = dp.LayoutSlot.Center.Y;}}if (!IsValidNumber(offsetY)){return false;}RadRect plotAreClip = series.Chart.PlotAreaClip;offsetY = (isInverse ? 1 : -1) * (plotAreClip.Height / 2);TranslateTransform transform = new TranslateTransform();transform.Y = offsetY;series.RenderTransform = transform;TimeSpan? beginTime = useDelay ? CalculateBeginTime(series) : null;series.Opacity = 0;DoubleAnimation transformAnimation = new DoubleAnimation();transformAnimation.From = offsetY;transformAnimation.To = 0;transformAnimation.Duration = AnimationDuration;transformAnimation.EasingFunction = new CircleEase() { EasingMode = EasingMode.EaseOut, };Storyboard.SetTargetProperty(transformAnimation, new PropertyPath(ChartAnimationUtilities.SeriesTranslateTransformYProperty));Storyboard.SetTarget(transformAnimation, series);DoubleAnimation opacityAnimation = new DoubleAnimation();opacityAnimation.From = 0;opacityAnimation.To = 1;opacityAnimation.Duration = AnimationDuration;opacityAnimation.EasingFunction = new CircleEase() { EasingMode = EasingMode.EaseOut, };Storyboard.SetTargetProperty(opacityAnimation, new PropertyPath(ChartSeries.OpacityProperty));Storyboard.SetTarget(opacityAnimation, series);Storyboard storyboard = new Storyboard();storyboard.Children.Add(transformAnimation);storyboard.Children.Add(opacityAnimation);if (beginTime != null){storyboard.BeginTime = beginTime;}Action completed = () => series.Opacity = 1;bool started = Run(storyboard, series, completed);if (!started){completed();}return started;}private static bool TryRunStackedBarsAnimtation(CartesianSeries series){Canvas renderSurface = Telerik.Windows.Controls.ChildrenOfTypeExtensions.FindChildByType<Canvas>(series);List<FrameworkElement> bars = new List<FrameworkElement>();foreach (FrameworkElement uiElement in renderSurface.Children){Border bar = uiElement as Border;ContentPresenter cp = uiElement as ContentPresenter;if ((bar != null && (bar.DataContext is CategoricalDataPoint)) ||(cp != null && cp.Content is CategoricalDataPoint)){bars.Add(uiElement);}}Storyboard storyboard = new Storyboard();RadCartesianChart chart = (RadCartesianChart)series.Chart;int initialDelay = (int)(BarAnimationDuration.TimeSpan.Milliseconds * chart.Series.IndexOf(series));TimeSpan? beginTime = TimeSpan.FromMilliseconds(initialDelay);for (int i = 0; i < bars.Count; i++){var animation = BuildStackedBarAnimation(bars[i], beginTime, BarAnimationDuration, series);storyboard.Children.Add(animation);beginTime = new TimeSpan(0, 0, 0, 0, initialDelay + (BarDelayInMilliseconds * (i + 1)));}return Run(storyboard, series);}private static bool Run(Storyboard storyboard, ChartSeries series, Action completed = null){if (storyboard.Children.Count == 0){return false;}storyboard.Completed += (s, e) =>{storyboard.Stop();lock (locker){int count = (int)series.GetValue(ChartAnimationUtilities.RunningAnimationsCountProperty);count--;series.SetValue(ChartAnimationUtilities.RunningAnimationsCountProperty, count);}if (completed != null){completed();}};storyboard.Begin();lock (locker){int count = (int)series.GetValue(ChartAnimationUtilities.RunningAnimationsCountProperty);count++;series.SetValue(ChartAnimationUtilities.RunningAnimationsCountProperty, count);}return true;}private static DoubleAnimation BuildSliceAnimation(Path path, TimeSpan? beginTime, Duration duration, Point center, PieSeries series){var scaleTransform = new ScaleTransform();scaleTransform.ScaleX = 0;scaleTransform.ScaleY = 0;scaleTransform.CenterX = center.X;scaleTransform.CenterY = center.Y;path.RenderTransform = scaleTransform;DoubleAnimation animation = new DoubleAnimation();animation.From = 0;animation.To = 1;animation.Duration = duration;animation.EasingFunction = new BackEase() { EasingMode = EasingMode.EaseOut, Amplitude = 0.4 };if (beginTime != null){animation.BeginTime = beginTime;}Storyboard.SetTarget(animation, path);Storyboard.SetTargetProperty(animation, new PropertyPath(ChartAnimationUtilities.SliceScaleTransformXYProperty));return animation;}private static DoubleAnimation BuildStackedBarAnimation(FrameworkElement bar, TimeSpan? beginTime, Duration duration, CartesianSeries series){bool isHorizontalBar = !IsSeriesHorizontal(series);bool isInverse = IsNumericalAxisInverse(series);double centerX = 0;double centerY = 0;DataPoint dp = bar.DataContext as DataPoint;if (dp == null){dp = (DataPoint)((bar as ContentPresenter).Content);}if (isHorizontalBar){centerX = isInverse ? dp.LayoutSlot.Width : 0;}else{centerY = isInverse ? 0 : dp.LayoutSlot.Height;}var scaleTransform = new ScaleTransform();scaleTransform.ScaleX = isHorizontalBar ? 0 : 1;scaleTransform.ScaleY = isHorizontalBar ? 1 : 0;scaleTransform.CenterX = centerX;scaleTransform.CenterY = centerY;bar.RenderTransform = scaleTransform;DoubleAnimation animation = new DoubleAnimation();animation.From = 0;animation.To = 1;animation.Duration = duration;if (beginTime != null){animation.BeginTime = beginTime;}Storyboard.SetTarget(animation, bar);if (isHorizontalBar){Storyboard.SetTargetProperty(animation, new PropertyPath(ChartAnimationUtilities.BarScaleTransformXProperty));}else{Storyboard.SetTargetProperty(animation, new PropertyPath(ChartAnimationUtilities.BarScaleTransformYProperty));}return animation;}private static TimeSpan? CalculateBeginTime(CartesianSeries series){RadCartesianChart chart = (RadCartesianChart)series.Chart;if (chart.Series.Count == 1){return null;}int delay = DelayInMilliseconds * chart.Series.IndexOf(series) / chart.Series.Count;return new TimeSpan(0, 0, 0, 0, delay);}private static IList<DataPoint> GetDataPoints(ChartSeries series){PieSeries pieSeries = series as PieSeries;if (pieSeries != null){return (IList<DataPoint>)pieSeries.DataPoints;}CategoricalSeries categoricalSeries = series as CategoricalSeries;if (categoricalSeries != null){return categoricalSeries.DataPoints;}RangeSeries rangeSeries = series as RangeSeries;if (rangeSeries != null){return rangeSeries.DataPoints;}OhlcSeries ohlcSeries = series as OhlcSeries;if (ohlcSeries != null){return ohlcSeries.DataPoints;}ScatterPointSeries scatterPointSeries = (ScatterPointSeries)series;return scatterPointSeries.DataPoints;}private static bool IsNumericalAxisInverse(CartesianSeries series){NumericalAxis axis = series.VerticalAxis as NumericalAxis;if (axis != null){return axis.IsInverse;}axis = ((RadCartesianChart)series.Chart).VerticalAxis as NumericalAxis;if (axis != null){return axis.IsInverse;}axis = series.HorizontalAxis as NumericalAxis;if (axis != null){return axis.IsInverse;}axis = ((RadCartesianChart)series.Chart).HorizontalAxis as NumericalAxis;if (axis != null){return axis.IsInverse;}return false;}private static bool IsSeriesHorizontal(CartesianSeries series){NumericalAxis axis = series.VerticalAxis as NumericalAxis;if (axis != null){return true;}axis = ((RadCartesianChart)series.Chart).VerticalAxis as NumericalAxis;if (axis != null){return true;}return false;}private static double CalculateHorizontalSeriesMiddle(CartesianSeries series){return CalculateSeriesMiddle(series, dp => dp.LayoutSlot.Center.X);}private static double CalculateVerticalSeriesMiddle(CartesianSeries series){return CalculateSeriesMiddle(series, dp => dp.LayoutSlot.Center.Y);}private static double CalculateSeriesMiddle(CartesianSeries series, Func<DataPoint, double> position){double min = double.PositiveInfinity;double max = double.NegativeInfinity;IList<DataPoint> dataPoints = GetDataPoints(series);foreach (DataPoint dp in dataPoints){if (dp.IsInPlotRange){double pos = position(dp);if (pos < min){min = pos;}if (max < pos){max = pos;}}}return (min + max) / 2;}private static bool IsValidNumber(double value){return double.MinValue <= value && value <= double.MaxValue;}} }總結
以上是生活随笔為你收集整理的使用Telerik控件库制作WPF项目中的折线图、柱状图、饼图和甜甜圈图的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Error in rq.fit.br(w
- 下一篇: 电压互感器二次侧不能短路运行,电流互感器