原始碼地址:https://gitee.com/LiuShuiRuoBing/code_blog
public IEnumerable<double> LayersPercentList { get { return (IEnumerable<double>)GetValue(LayersPercentListProperty); } set { SetValue(LayersPercentListProperty, value); } } // Using a DependencyProperty as the backing store for MyProperty. This enables animation, styling, binding, etc... public static readonly DependencyProperty LayersPercentListProperty = DependencyProperty.Register("LayersPercentList", typeof(IEnumerable<double>), typeof(RadarMapUserControl), new FrameworkPropertyMetadata(null, new PropertyChangedCallback(OnChangedToRefreshRadarMap)));
<UserControl x:Class="MT.CustomUserControl.Views.UserControls.RadarMapUserControl" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:local="clr-namespace:MT.CustomUserControl.Views.UserControls" mc:Ignorable="d" d:Background="White" SizeChanged="RadarMapUserControl_SizeChanged" > <!--雷達圖--> <Grid x:Name="Grid_RadarMap" HorizontalAlignment="Center" VerticalAlignment="Center" MinWidth="300" MinHeight="300" Margin="200" SizeChanged="RadarMapUserControl_SizeChanged" /> </UserControl>
using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.Linq; using System.Windows; using System.Windows.Controls; using System.Windows.Media; using System.Windows.Shapes; namespace MT.CustomUserControl.Views.UserControls { /// <summary> /// QSCC_RadarMapUserControl.xaml 的互動邏輯 /// </summary> public partial class RadarMapUserControl : UserControl { #region 私有屬性,用於繪圖 /// <summary> /// 每個磁區的角度 /// </summary> private double Angle { set; get; } /// <summary> /// 用於繪製雷達圖的層數的多邊形 /// </summary> private List<Polygon> RadarMapLayersPolygon = new List<Polygon>(); /// <summary> /// 用於繪製雷達圖的射線 /// </summary> private List<Polyline> RadarMapRadialsPolyline = new List<Polyline>(); /// <summary> /// 用於繪製雷達圖射線上實際值的圓點,使用多邊形繪製,以實際值為圓心擴充套件多變形 /// </summary> private List<Polygon> RadarMapRadialsValuesPolygons = new List<Polygon>(); /// <summary> /// 所有的雷達圖的多變形 /// </summary> private Polygon RadarMapRadialsValuesPolygon = new Polygon(); #endregion #region 雷達圖圖層 /// <summary> /// 雷達圖的層數 /// </summary> public int Layers { get { return (int)GetValue(LayersProperty); } set { if (value < 1) value = 1; SetValue(LayersProperty, value); } } // Using a DependencyProperty as the backing store for Layers. This enables animation, styling, binding, etc... public static readonly DependencyProperty LayersProperty = DependencyProperty.Register("Layers", typeof(int), typeof(RadarMapUserControl), new PropertyMetadata(4, new PropertyChangedCallback(OnCurrentLayersChanged))); private static void OnCurrentLayersChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { RadarMapUserControl userControl = (RadarMapUserControl)d; bool needRefresh = false; if (userControl.RadarMapLayersPolygon.Count > userControl.Layers) { int nCnt = userControl.RadarMapLayersPolygon.Count - userControl.Layers; userControl.RadarMapLayersPolygon.RemoveRange(userControl.RadarMapLayersPolygon.Count - 1 - nCnt, nCnt); needRefresh = true; } else if (userControl.RadarMapLayersPolygon.Count < userControl.Layers) { int nCnt = userControl.Layers - userControl.RadarMapLayersPolygon.Count; for (int i = 0; i < nCnt; i++) { userControl.RadarMapLayersPolygon.Add(new Polygon() { Stroke = userControl.LayerStroke, StrokeThickness = 1 }); } needRefresh = true; } if (needRefresh) { userControl.RefreshRadarMap(); } } /// <summary> /// 雷達圖分層的規則,這裡使用0-1之間的資料標識,主要是用比例來表示 /// 在使用者未指定的情況下,則根據Layers的層數來均分 /// 設定舉例:雷達圖分4層,均分每層面積,則LayersPercentList設定為: /// LayersPercentList[0] = 0.25; /// LayersPercentList[1] = 0.5; /// LayersPercentList[2] = 0.75; /// LayersPercentList[3] = 1; /// </summary> public IEnumerable<double> LayersPercentList { get { return (IEnumerable<double>)GetValue(LayersPercentListProperty); } set { SetValue(LayersPercentListProperty, value); } } // Using a DependencyProperty as the backing store for MyProperty. This enables animation, styling, binding, etc... public static readonly DependencyProperty LayersPercentListProperty = DependencyProperty.Register("LayersPercentList", typeof(IEnumerable<double>), typeof(RadarMapUserControl), new FrameworkPropertyMetadata(null, new PropertyChangedCallback(OnChangedToRefreshRadarMap))); /// <summary> /// 每層邊框的粗細 /// </summary> public double LayerStrokeThickness { get { return (double)GetValue(LayerStrokeThicknessProperty); } set { SetValue(LayerStrokeThicknessProperty, value); } } // Using a DependencyProperty as the backing store for LayerStrokeThickness. This enables animation, styling, binding, etc... public static readonly DependencyProperty LayerStrokeThicknessProperty = DependencyProperty.Register("LayerStrokeThickness", typeof(double), typeof(RadarMapUserControl), new PropertyMetadata(1.0, new PropertyChangedCallback(OnCurrentLayersFillBrushAndStockThicknessChanged))); /// <summary> /// 每層的邊框顏色 /// </summary> public SolidColorBrush LayerStroke { get { return (SolidColorBrush)GetValue(LayerStrokeProperty); } set { SetValue(LayerStrokeProperty, value); } } // Using a DependencyProperty as the backing store for LayerStroke. This enables animation, styling, binding, etc... public static readonly DependencyProperty LayerStrokeProperty = DependencyProperty.Register("LayerStroke", typeof(SolidColorBrush), typeof(RadarMapUserControl), new PropertyMetadata(Brushes.White, new PropertyChangedCallback(OnCurrentLayersFillBrushAndStockThicknessChanged))); /// <summary> /// 雷達圖從內到外漸變色,內部顏色 /// </summary> public Color InnerColor { get { return (Color)GetValue(InnerColorProperty); } set { SetValue(InnerColorProperty, value); } } // Using a DependencyProperty as the backing store for InnerColor. This enables animation, styling, binding, etc... public static readonly DependencyProperty InnerColorProperty = DependencyProperty.Register("InnerColor", typeof(Color), typeof(RadarMapUserControl), new PropertyMetadata(Colors.White, new PropertyChangedCallback(OnCurrentLayersFillBrushAndStockThicknessChanged))); /// <summary> /// 雷達圖從內到外漸變色,外部顏色 /// </summary> public Color OutColor { get { return (Color)GetValue(OutColorProperty); } set { SetValue(OutColorProperty, value); } } // Using a DependencyProperty as the backing store for OutColor. This enables animation, styling, binding, etc... public static readonly DependencyProperty OutColorProperty = DependencyProperty.Register("OutColor", typeof(Color), typeof(RadarMapUserControl), new PropertyMetadata(Colors.Purple, new PropertyChangedCallback(OnCurrentLayersFillBrushAndStockThicknessChanged))); private static void OnCurrentLayersFillBrushAndStockThicknessChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { RadarMapUserControl userControl = (RadarMapUserControl)d; userControl.RefreshLayersFillBrushAndThickness(); } #endregion #region 雷達圖射線 /// <summary> /// 雷達圖的射線數 /// </summary> public int Radials { get { return (int)GetValue(RadialsProperty); } set { SetValue(RadialsProperty, value); } } // Using a DependencyProperty as the backing store for Radials. This enables animation, styling, binding, etc... public static readonly DependencyProperty RadialsProperty = DependencyProperty.Register("Radials", typeof(int), typeof(RadarMapUserControl), new PropertyMetadata(9, new PropertyChangedCallback(OnCurrentRadialsChanged))); private static void OnCurrentRadialsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { RadarMapUserControl userControl = (RadarMapUserControl)d; bool needRefresh = false; if (userControl.RadarMapRadialsPolyline.Count > userControl.Radials) { int nCnt = userControl.RadarMapRadialsPolyline.Count - userControl.Radials; userControl.RadarMapRadialsPolyline.RemoveRange(userControl.RadarMapRadialsPolyline.Count - 1 - nCnt, nCnt); needRefresh = true; } else if (userControl.RadarMapRadialsPolyline.Count < userControl.Radials) { int nCnt = userControl.Radials - userControl.RadarMapRadialsPolyline.Count; for (int i = 0; i < nCnt; i++) { userControl.RadarMapRadialsPolyline.Add(new Polyline() { Stroke = userControl.RadialBrush, StrokeThickness = 2 }); } needRefresh = true; } if (userControl.RadarMapRadialsValuesPolygons.Count > userControl.Radials) { int nCnt = userControl.RadarMapRadialsValuesPolygons.Count - userControl.Radials; userControl.RadarMapRadialsValuesPolygons.RemoveRange(userControl.RadarMapRadialsValuesPolygons.Count - 1 - nCnt, nCnt); needRefresh = true; } else if (userControl.RadarMapRadialsValuesPolygons.Count < userControl.Radials) { int nCnt = userControl.Radials - userControl.RadarMapRadialsValuesPolygons.Count; for (int i = 0; i < nCnt; i++) { userControl.RadarMapRadialsValuesPolygons.Add(new Polygon() { Stroke = userControl.LayerStroke, StrokeThickness = 1 }); } needRefresh = true; } if (needRefresh) userControl.RefreshRadarMap(); } /// <summary> /// 雷達圖半徑,決定雷達圖的半徑 /// </summary> public double Radius { get { return (double)GetValue(RadiusProperty); } set { SetValue(RadiusProperty, value); } } // Using a DependencyProperty as the backing store for Radius. This enables animation, styling, binding, etc... public static readonly DependencyProperty RadiusProperty = DependencyProperty.Register("Radius", typeof(double), typeof(RadarMapUserControl), new PropertyMetadata(100.0, new PropertyChangedCallback(OnChangedToRefreshRadarMap))); /// <summary> /// 射線顏色 /// </summary> public SolidColorBrush RadialBrush { get { return (SolidColorBrush)GetValue(RadialBrushProperty); } set { SetValue(RadialBrushProperty, value); } } // Using a DependencyProperty as the backing store for RadialBrush. This enables animation, styling, binding, etc... public static readonly DependencyProperty RadialBrushProperty = DependencyProperty.Register("RadialBrush", typeof(SolidColorBrush), typeof(RadarMapUserControl), new PropertyMetadata(Brushes.White, new PropertyChangedCallback(OnCurrentRadialBrushAndThicknessChanged))); /// <summary> /// 射線粗細 /// </summary> public double RadialThickness { get { return (double)GetValue(RadialThicknessProperty); } set { SetValue(RadialThicknessProperty, value); } } // Using a DependencyProperty as the backing store for RadialThickness. This enables animation, styling, binding, etc... public static readonly DependencyProperty RadialThicknessProperty = DependencyProperty.Register("RadialThickness", typeof(double), typeof(RadarMapUserControl), new PropertyMetadata(1.5, new PropertyChangedCallback(OnCurrentRadialBrushAndThicknessChanged))); private static void OnCurrentRadialBrushAndThicknessChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { RadarMapUserControl userControl = (RadarMapUserControl)d; userControl.RefreshRadialBrushAndThinkness(); } #endregion #region 雷達圖上點 /// <summary> /// 射線上的所有值點 /// 1. 注意在使用繫結時,要先將Binding物件設定為null,然後將資料整合好的ObservableCollection再賦值給繫結物件,否則不更新 /// </summary> public IEnumerable<double> Values { get { return (IEnumerable<double>)GetValue(ValuesProperty); } set { SetValue(ValuesProperty, value); } } // Using a DependencyProperty as the backing store for Values. This enables animation, styling, binding, etc... public static readonly DependencyProperty ValuesProperty = DependencyProperty.Register("Values", typeof(IEnumerable<double>), typeof(RadarMapUserControl), new FrameworkPropertyMetadata(null, new PropertyChangedCallback(OnChangedToRefreshValues))); private static void OnChangedToRefreshValues(DependencyObject d, DependencyPropertyChangedEventArgs e) { RadarMapUserControl userControl = (RadarMapUserControl)d; userControl.DrawRadarMapRadialsValues(); } /// <summary> /// 普通值點的繪製半徑 /// </summary> public double ValueRadius { get { return (double)GetValue(ValueRadiusProperty); } set { SetValue(ValueRadiusProperty, value); } } // Using a DependencyProperty as the backing store for ValueRadius. This enables animation, styling, binding, etc... public static readonly DependencyProperty ValueRadiusProperty = DependencyProperty.Register("ValueRadius", typeof(double), typeof(RadarMapUserControl), new PropertyMetadata(4.0, new PropertyChangedCallback(OnChangedToRefreshValueRadiusAndBrush))); /// <summary> /// 普通值點的顏色 /// </summary> public SolidColorBrush ValueBrush { get { return (SolidColorBrush)GetValue(ValueBrushProperty); } set { SetValue(ValueBrushProperty, value); } } // Using a DependencyProperty as the backing store for ValueBrush. This enables animation, styling, binding, etc... public static readonly DependencyProperty ValueBrushProperty = DependencyProperty.Register("ValueBrush", typeof(SolidColorBrush), typeof(RadarMapUserControl), new PropertyMetadata(Brushes.Red, new PropertyChangedCallback(OnChangedToRefreshValueRadiusAndBrush))); /// <summary> /// 需要高亮點的索引 /// 1. 注意在使用繫結時,要先將Binding物件設定為null,然後將資料整合好的ObservableCollection再賦值給繫結物件,否則不更新 /// </summary> public IEnumerable<double> HeightLightValues { get { return (IEnumerable<double>)GetValue(HeightLightValuesProperty); } set { SetValue(HeightLightValuesProperty, value); } } // Using a DependencyProperty as the backing store for HeightLightPoints. This enables animation, styling, binding, etc... public static readonly DependencyProperty HeightLightValuesProperty = DependencyProperty.Register("HeightLightValues", typeof(IEnumerable<double>), typeof(RadarMapUserControl), new FrameworkPropertyMetadata(null, new PropertyChangedCallback(OnChangedToRefreshValues))); /// <summary> /// 光亮點的半徑 /// </summary> public double HeighLightRadius { get { return (double)GetValue(HeighLightRadiusProperty); } set { SetValue(HeighLightRadiusProperty, value); } } // Using a DependencyProperty as the backing store for HeighLightValueRadius. This enables animation, styling, binding, etc... public static readonly DependencyProperty HeighLightRadiusProperty = DependencyProperty.Register("HeighLightRadius", typeof(double), typeof(RadarMapUserControl), new PropertyMetadata(6.0, new PropertyChangedCallback(OnChangedToRefreshValueRadiusAndBrush))); /// <summary> /// 高亮點的顏色 /// </summary> public SolidColorBrush HeighLightBrush { get { return (SolidColorBrush)GetValue(HeighLightBrushProperty); } set { SetValue(HeighLightBrushProperty, value); } } // Using a DependencyProperty as the backing store for MyProperty. This enables animation, styling, binding, etc... public static readonly DependencyProperty HeighLightBrushProperty = DependencyProperty.Register("HeighLightBrush", typeof(SolidColorBrush), typeof(RadarMapUserControl), new PropertyMetadata(Brushes.Yellow, new PropertyChangedCallback(OnChangedToRefreshValueRadiusAndBrush))); private static void OnChangedToRefreshValueRadiusAndBrush(DependencyObject d, DependencyPropertyChangedEventArgs e) { RadarMapUserControl userControl = (RadarMapUserControl)d; userControl.RefreshValuesRadiusAndBrush(); } #endregion #region 雷達圖值區域 /// <summary> /// 雷達圖值區域填充色 /// </summary> public SolidColorBrush ValuesAreaFill { get { return (SolidColorBrush)GetValue(ValuesAreaFillProperty); } set { SetValue(ValuesAreaFillProperty, value); } } // Using a DependencyProperty as the backing store for ValuesAreaFill. This enables animation, styling, binding, etc... public static readonly DependencyProperty ValuesAreaFillProperty = DependencyProperty.Register("ValuesAreaFill", typeof(SolidColorBrush), typeof(RadarMapUserControl), new PropertyMetadata(Brushes.Red, new PropertyChangedCallback(OnChangedToRefreshValuesAreaFillAndStrokeBrush))); /// <summary> /// 雷達圖值區域邊框色 /// </summary> public SolidColorBrush ValuesAreaStroke { get { return (SolidColorBrush)GetValue(ValuesAreaStrokeProperty); } set { SetValue(ValuesAreaStrokeProperty, value); } } // Using a DependencyProperty as the backing store for ValuesAreaStroke. This enables animation, styling, binding, etc... public static readonly DependencyProperty ValuesAreaStrokeProperty = DependencyProperty.Register("ValuesAreaStroke", typeof(SolidColorBrush), typeof(RadarMapUserControl), new PropertyMetadata(Brushes.Gray, new PropertyChangedCallback(OnChangedToRefreshValuesAreaFillAndStrokeBrush))); private static void OnChangedToRefreshValuesAreaFillAndStrokeBrush(DependencyObject d, DependencyPropertyChangedEventArgs e) { RadarMapUserControl userControl = (RadarMapUserControl)d; userControl.RefreshValuesAreaBrushAndStroke(); } #endregion #region 雷達圖的射線標題文字 /// <summary> /// 是否顯示Title /// </summary> public bool ShowTitle { get { return (bool)GetValue(ShowTitleProperty); } set { SetValue(ShowTitleProperty, value); } } // Using a DependencyProperty as the backing store for ShowTitle. This enables animation, styling, binding, etc... public static readonly DependencyProperty ShowTitleProperty = DependencyProperty.Register("ShowTitle", typeof(bool), typeof(RadarMapUserControl), new PropertyMetadata(false, new PropertyChangedCallback(OnChangedToRefreshTitles))); /// <summary> /// 文字的前景色 /// </summary> public SolidColorBrush TitleForground { get { return (SolidColorBrush)GetValue(TitleForgroundProperty); } set { SetValue(TitleForgroundProperty, value); } } // Using a DependencyProperty as the backing store for TitleForground. This enables animation, styling, binding, etc... public static readonly DependencyProperty TitleForgroundProperty = DependencyProperty.Register("TitleForground", typeof(SolidColorBrush), typeof(RadarMapUserControl), new PropertyMetadata(Brushes.Black, new PropertyChangedCallback(OnChangedToRefreshTitles))); /// <summary> /// 文字的字號 /// </summary> public int TitleFontSize { get { return (int)GetValue(TitleFontSizeProperty); } set { SetValue(TitleFontSizeProperty, value); } } // Using a DependencyProperty as the backing store for TitleFontSize. This enables animation, styling, binding, etc... public static readonly DependencyProperty TitleFontSizeProperty = DependencyProperty.Register("TitleFontSize", typeof(int), typeof(RadarMapUserControl), new PropertyMetadata(14, new PropertyChangedCallback(OnChangedToRefreshTitles))); /// <summary> /// FontWeight /// </summary> public FontWeight TitleFontWeight { get { return (FontWeight)GetValue(TitleFontWeightProperty); } set { SetValue(TitleFontWeightProperty, value); } } // Using a DependencyProperty as the backing store for TitleFontWeights. This enables animation, styling, binding, etc... public static readonly DependencyProperty TitleFontWeightProperty = DependencyProperty.Register("TitleFontWeights", typeof(FontWeight), typeof(RadarMapUserControl), new PropertyMetadata(FontWeights.Normal, new PropertyChangedCallback(OnChangedToRefreshTitles))); /// <summary> /// Title要顯示的文字 /// </summary> public IEnumerable<string> Titles { get { return (IEnumerable<string>)GetValue(TitlesProperty); } set { SetValue(TitlesProperty, value); } } // Using a DependencyProperty as the backing store for MyProperty. This enables animation, styling, binding, etc... public static readonly DependencyProperty TitlesProperty = DependencyProperty.Register("Titles", typeof(IEnumerable<string>), typeof(RadarMapUserControl), new FrameworkPropertyMetadata(null, new PropertyChangedCallback(OnChangedToRefreshTitles))); private static void OnChangedToRefreshTitles(DependencyObject d, DependencyPropertyChangedEventArgs e) { RadarMapUserControl userControl = (RadarMapUserControl)d; userControl.RefreshRadarMap(); } #endregion private static void OnChangedToRefreshRadarMap(DependencyObject d, DependencyPropertyChangedEventArgs e) { RadarMapUserControl userControl = (RadarMapUserControl)d; userControl.RefreshRadarMap(); } public RadarMapUserControl() { SolidColorBrush polygonFill = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#7653a7")); //繪製圖層 List<Color> colors = GetSingleColorList(OutColor, InnerColor, Layers); for (int i = 0; i < Layers; i++) { RadarMapLayersPolygon.Add(new Polygon() {Fill= new SolidColorBrush(colors[i]), Stroke = LayerStroke, StrokeThickness = LayerStrokeThickness }); } //繪製射線以及線上值 for (int i = 0; i < Radials; i++) { RadarMapRadialsPolyline.Add(new Polyline() { Stroke = RadialBrush, StrokeThickness = RadialThickness }); RadarMapRadialsValuesPolygons.Add(new Polygon() { Fill = ValueBrush, StrokeThickness = 1 }); } //雷達圖值組成的區域 RadarMapRadialsValuesPolygon = new Polygon() { Fill = ValuesAreaFill, Stroke = ValuesAreaStroke, Opacity = 0.2 }; InitializeComponent(); } private void RadarMapUserControl_SizeChanged(object sender, SizeChangedEventArgs e) { RefreshRadarMap(); } /// <summary> /// 重新整理雷達圖的層的填充色和層線粗細 /// </summary> private void RefreshLayersFillBrushAndThickness() { //繪製雷達圖層的多邊形 List<Color> colors = GetSingleColorList(OutColor, InnerColor, Layers); for (int i = 0; i < Layers; i++) { RadarMapLayersPolygon[i].Fill = new SolidColorBrush(colors[i]); RadarMapLayersPolygon[i].Stroke = LayerStroke; RadarMapLayersPolygon[i].StrokeThickness = LayerStrokeThickness; } } /// <summary> /// 重新整理射線的顏色和粗細 /// </summary> private void RefreshRadialBrushAndThinkness() { foreach (var item in RadarMapRadialsPolyline) { item.Stroke = RadialBrush; item.StrokeThickness = RadialThickness; } } /// <summary> /// 重新整理雷達圖 /// </summary> private void RefreshRadarMap() { Grid_RadarMap.Children.Clear(); //首先清除一下polygon裡儲存的資料 for (int i = 0; i < Layers; i++) { RadarMapLayersPolygon[i]?.Points?.Clear(); } for (int i = 0; i < Radials; i++) { RadarMapRadialsPolyline[i]?.Points?.Clear(); } //如果設定了LayersPercentList,並且LayersPercentList的元素個數與層數相同則按照LayersPercentList畫每層的佔比,否則均分每層佔比 List<double> layersPercents = new List<double>(); if (LayersPercentList != null && LayersPercentList.Count() == Layers && LayersPercentList.Max() < 1) { foreach (var item in LayersPercentList) { layersPercents.Add(item); } } else { double gap = 1.0 / Layers; for (int i = 0; i < Layers; i++) { layersPercents.Add(gap * i + gap); //計算每層的預設佔比 } } //計算每個磁區的角度 Angle = 360 / Radials; //計算並新增雷達圖的區域線和射線上的點 for (int i = 0; i < Radials; i++) { //射線上每層的點,從外到內 List<Point> points = new List<Point>(); for (int j = 0; j < Layers; j++) { Point p = new Point(Radius + (Radius * layersPercents[Layers - j - 1]) * Math.Cos((Angle * i - 90) * Math.PI / 180), Radius + (Radius * layersPercents[Layers - j - 1]) * Math.Sin((Angle * i - 90) * Math.PI / 180)); points.Add(p); //新增到區域線中 RadarMapLayersPolygon[j].Points.Add(p); } //新增到射線中 foreach (var item in points) { RadarMapRadialsPolyline[i].Points.Add(item); } //計算原點並新增到射線中 Point p_origin = new Point(Radius + Radius * 0 * Math.Cos((Angle * i - 90) * Math.PI / 180), Radius + Radius * 0 * Math.Sin((Angle * i - 90) * Math.PI / 180)); RadarMapRadialsPolyline[i].Points.Add(p_origin); } //繪製區域層 foreach (var polygon in RadarMapLayersPolygon) { if (!Grid_RadarMap.Children.Contains(polygon)) Grid_RadarMap.Children.Add(polygon); } //繪製雷達圖射線 foreach (var polyline in RadarMapRadialsPolyline) { if (!Grid_RadarMap.Children.Contains(polyline)) Grid_RadarMap.Children.Add(polyline); } //繪製雷達圖上的文字 if (ShowTitle && Titles != null && Titles.Count() == Radials) { List<string> titleList = Titles.ToList(); for (int i = 0; i < Radials; i++) { Point point = RadarMapLayersPolygon[0].Points[i]; string title = titleList[i]; TextBlock textBlock = RefreshRadiusTitles(point, title); if (!Grid_RadarMap.Children.Contains(textBlock)) Grid_RadarMap.Children.Add(textBlock); } } DrawRadarMapRadialsValues(); } /// <summary> /// 重新整理雷達圖上值的點的半徑和填充色,以及高亮點的半徑和填充色 /// </summary> private void RefreshValuesRadiusAndBrush() { if (Values == null) return; bool drawHeight = false; if (HeightLightValues != null && HeightLightValues.Count() > 0) drawHeight = true; List<double> values = Values.ToList(); for (int i = 0; i < RadarMapRadialsValuesPolygon.Points.Count; i++) { RadarMapRadialsValuesPolygons[i].Points.Clear(); RadarMapRadialsValuesPolygons[i].Fill = ValueBrush; double radius = ValueRadius; if (drawHeight) { if (HeightLightValues.Contains(values[i])) { radius = HeighLightRadius; RadarMapRadialsValuesPolygons[i].Fill = HeighLightBrush; if (ShowTitle && Titles != null && Titles.Count() > i) { List<string> titleList = Titles.ToList(); string heightTitle = titleList[i]; foreach (var item in Grid_RadarMap.Children) { if (item is TextBlock) { TextBlock textBlock = (TextBlock)item; if (textBlock.Text == heightTitle) { textBlock.Foreground = HeighLightBrush; } } } } } } Point valuePoint = RadarMapRadialsValuesPolygon.Points[i]; Point[] calc_points = GetEllipsePoints(valuePoint, radius); foreach (var p in calc_points) { RadarMapRadialsValuesPolygons[i].Points.Add(p); } if (!Grid_RadarMap.Children.Contains(RadarMapRadialsValuesPolygons[i])) Grid_RadarMap.Children.Add(RadarMapRadialsValuesPolygons[i]); } } /// <summary> /// 重新整理雷達圖值區域的填充色和邊框色 /// </summary> private void RefreshValuesAreaBrushAndStroke() { RadarMapRadialsValuesPolygon.Fill = ValuesAreaFill; RadarMapRadialsValuesPolygon.Stroke = ValuesAreaStroke; } /// <summary> /// 重新整理射線上的文字標題 /// </summary> /// <param name="point">圖層最外層的點</param> /// <returns></returns> private TextBlock RefreshRadiusTitles(Point point, string title) { TextBlock textBlock = new TextBlock(); textBlock.FontSize = 20; textBlock.Text = title; textBlock.Foreground = TitleForground; textBlock.FontWeight = FontWeights.Normal; textBlock.FontSize = TitleFontSize; //計算文字的實際畫素值 Rect rect1 = new Rect(); textBlock.Arrange(rect1); double textLength = textBlock.ActualWidth; Thickness thickness = new Thickness(point.X + 10, point.Y - 10, 0, 0); if (point.X == Radius && point.Y < Radius) { thickness = new Thickness(point.X - textLength / 2, point.Y - 30, 0, 0); } else if (point.X == Radius && point.Y >= Radius) { thickness = new Thickness(point.X - textLength / 2, point.Y + 10, 0, 0); } else if (point.X < Radius) { thickness = new Thickness(point.X - 20 - textLength, point.Y - 10, 0, 0); } else { thickness = new Thickness(point.X + 10, point.Y - 10, 0, 0); } textBlock.Margin = thickness; return textBlock; } /// <summary> /// 繪製雷達圖上的點 /// </summary> /// <param name="Values"></param> /// <param name="mainType"></param> /// <param name="secondType"></param> public void DrawRadarMapRadialsValues() { if (Values == null || Values.Count() != Radials) return; int fullScore = 100; RadarMapRadialsValuesPolygon.Points.Clear(); for (int i = 0; i < Radials; i++) { double temp = Values.ToList()[i]; if (temp <= 0) continue; Point value = new Point(Radius + Radius * (temp * 1.0 / fullScore) * Math.Cos((Angle * i - 90) * Math.PI / 180), Radius + Radius * (temp * 1.0 / fullScore) * Math.Sin((Angle * i - 90) * Math.PI / 180)); RadarMapRadialsValuesPolygon.Points.Add(value); } if (!Grid_RadarMap.Children.Contains(RadarMapRadialsValuesPolygon)) Grid_RadarMap.Children.Add(RadarMapRadialsValuesPolygon); RefreshValuesRadiusAndBrush(); } #region 工具類 /// <summary> /// 根據圓心,擴充套件繪製圓 /// </summary> /// <param name="origin"></param> /// <param name="radius"></param> /// <returns></returns> private Point[] GetEllipsePoints(Point origin, double radius) { int count = 10; Point[] points = new Point[count]; double angle = 360 / count; for (int i = 0; i < count; i++) { Point p1 = new Point(origin.X + radius * Math.Cos((angle * i - 90) * Math.PI / 180), origin.Y + radius * Math.Sin((angle * i - 90) * Math.PI / 180)); points[i] = p1; } return points; } /// <summary> /// 獲得某一顏色區間的顏色集合 /// </summary> /// <param name="sourceColor">起始顏色</param> /// <param name="destColor">終止顏色</param> /// <param name="count">分度數</param> /// <returns>返回顏色集合</returns> private List<Color> GetSingleColorList(Color srcColor, Color desColor, int count) { List<Color> colorFactorList = new List<Color>(); int redSpan = desColor.R - srcColor.R; int greenSpan = desColor.G - srcColor.G; int blueSpan = desColor.B - srcColor.B; for (int i = 0; i < count; i++) { Color color = Color.FromRgb( (byte)(srcColor.R + (int)((double)i / count * redSpan)), (byte)(srcColor.G + (int)((double)i / count * greenSpan)), (byte)(srcColor.B + (int)((double)i / count * blueSpan)) ); colorFactorList.Add(color); } return colorFactorList; } #endregion } }