WPF自定義實(shí)現(xiàn)雷達(dá)圖控件的示例詳解
效果圖

源碼地址:https://gitee.com/LiuShuiRuoBing/code_blog
雷達(dá)圖用于表示不同內(nèi)容的占比關(guān)系,在項(xiàng)目中有廣泛的應(yīng)用,但是目前未曾有封裝良好的雷達(dá)圖控件,鑒于最近項(xiàng)目的使用,于是想要封裝一個(gè)通用的雷達(dá)圖控件,便于日后的擴(kuò)展使用。
首先雷達(dá)圖的繪制大概分為雷達(dá)圖的圖層、雷達(dá)圖的射線、雷達(dá)圖的有效值區(qū)域、雷達(dá)圖有效值在射線上的點(diǎn)、標(biāo)題文字等幾個(gè)部分,封裝的初衷就是想要每個(gè)部分足夠靈活,像圖層層數(shù)、顏色、射線條數(shù)、顏色、粗細(xì)文字顏色等屬性都開發(fā)出來,允許使用者自行設(shè)置。這就需要給雷達(dá)圖控件定義各種不同的依賴屬性。
使用自定義控件來實(shí)現(xiàn)雷達(dá)圖的封裝,首先創(chuàng)建UserControl,并使用Grid來最為父級容器控件,這里之所以選用Grid而不是Canvas,是因?yàn)镃anvas上的控件屬于絕對定位,而在Grid中更多的是相對定位,查看很多原生空間的樣式或者模板會看到更多的使用布局控件作為底層父級容器控件。
關(guān)于控件的自定義依賴屬性的知識點(diǎn),在此不再贅述,說幾個(gè)需要注意的地方,
- 當(dāng)要創(chuàng)建可以Binding的集合時(shí),需要定義IEnumerable的類型,注冊時(shí)PropertyMetadata要使用FrameworkPropertyMetadata
- 當(dāng)需要修改屬性值,UI會隨之更新時(shí),要在注冊時(shí),注入PropertyChangedCallback函數(shù)
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)));1.雷達(dá)圖層的繪制
雷達(dá)圖層即雷達(dá)圖有幾層,每一層會有不同的占比,每一層的顏色也不相同,因此分別定義了下面的依賴屬性,來對雷達(dá)圖層進(jìn)行設(shè)置,詳細(xì)解釋請看代碼
- Layers 雷達(dá)圖層數(shù)
- LayersPercentList 每一層的占比
- LayerStrokeThickness 每層邊框的粗細(xì)
- LayerStroke 每層的邊框顏色
- InnerColor 雷達(dá)圖從內(nèi)到外的漸變色,內(nèi)部顏色
- OutColor 雷達(dá)圖從內(nèi)到外的漸變色,外部顏色
2.雷達(dá)圖射線
- Radials 雷達(dá)圖的射線數(shù)
- Radius 雷達(dá)圖半徑
- RadialBrush 射線顏色
- RadialThickness 射線粗細(xì)
3.雷達(dá)圖上的點(diǎn)
- Values ,普通點(diǎn)的集合,可以綁定
- ValueRadius 普通點(diǎn)的繪制半徑
- ValueBrush 普通點(diǎn)的填充色
- HeightLightValues, 希望高亮點(diǎn)的集合
- HeighLightRadius,高亮點(diǎn)的半徑
- HeighLightBrush,高亮點(diǎn)的
4.雷達(dá)圖上的值區(qū)域
- ValuesAreaFill 值區(qū)域的填充色
- ValuesAreaStroke 值區(qū)域的邊框色
5.雷達(dá)圖的射線標(biāo)題文字
- ShowTitle 是否顯示標(biāo)題文字
- TitleForground 文字的前景色
- TitleFontSize 文字的字號
- TitleFontWeight 字重
- Titles ,集合, 每條射線要現(xiàn)實(shí)的文字
RadarMapUserControl.xaml 完整代碼
<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"
>
<!--雷達(dá)圖-->
<Grid x:Name="Grid_RadarMap" HorizontalAlignment="Center" VerticalAlignment="Center" MinWidth="300" MinHeight="300" Margin="200" SizeChanged="RadarMapUserControl_SizeChanged" />
</UserControl>RadarMapUserControl.cs 完整代碼
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>
/// 每個(gè)扇區(qū)的角度
/// </summary>
private double Angle { set; get; }
/// <summary>
/// 用于繪制雷達(dá)圖的層數(shù)的多邊形
/// </summary>
private List<Polygon> RadarMapLayersPolygon = new List<Polygon>();
/// <summary>
/// 用于繪制雷達(dá)圖的射線
/// </summary>
private List<Polyline> RadarMapRadialsPolyline = new List<Polyline>();
/// <summary>
/// 用于繪制雷達(dá)圖射線上實(shí)際值的圓點(diǎn),使用多邊形繪制,以實(shí)際值為圓心擴(kuò)展多變形
/// </summary>
private List<Polygon> RadarMapRadialsValuesPolygons = new List<Polygon>();
/// <summary>
/// 所有的雷達(dá)圖的多變形
/// </summary>
private Polygon RadarMapRadialsValuesPolygon = new Polygon();
#endregion
#region 雷達(dá)圖圖層
/// <summary>
/// 雷達(dá)圖的層數(shù)
/// </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>
/// 雷達(dá)圖分層的規(guī)則,這里使用0-1之間的數(shù)據(jù)標(biāo)識,主要是用比例來表示
/// 在使用者未指定的情況下,則根據(jù)Layers的層數(shù)來均分
/// 設(shè)置舉例:雷達(dá)圖分4層,均分每層面積,則LayersPercentList設(shè)置為:
/// 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>
/// 每層邊框的粗細(xì)
/// </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>
/// 雷達(dá)圖從內(nèi)到外漸變色,內(nèi)部顏色
/// </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>
/// 雷達(dá)圖從內(nèi)到外漸變色,外部顏色
/// </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 雷達(dá)圖射線
/// <summary>
/// 雷達(dá)圖的射線數(shù)
/// </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>
/// 雷達(dá)圖半徑,決定雷達(dá)圖的半徑
/// </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>
/// 射線粗細(xì)
/// </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 雷達(dá)圖上點(diǎn)
/// <summary>
/// 射線上的所有值點(diǎn)
/// 1. 注意在使用綁定時(shí),要先將Binding對象設(shè)置為null,然后將數(shù)據(jù)整合好的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>
/// 普通值點(diǎn)的繪制半徑
/// </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>
/// 普通值點(diǎn)的顏色
/// </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>
/// 需要高亮點(diǎn)的索引
/// 1. 注意在使用綁定時(shí),要先將Binding對象設(shè)置為null,然后將數(shù)據(jù)整合好的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>
/// 光亮點(diǎn)的半徑
/// </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>
/// 高亮點(diǎn)的顏色
/// </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 雷達(dá)圖值區(qū)域
/// <summary>
/// 雷達(dá)圖值區(qū)域填充色
/// </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>
/// 雷達(dá)圖值區(qū)域邊框色
/// </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 雷達(dá)圖的射線標(biāo)題文字
/// <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 });
}
//雷達(dá)圖值組成的區(qū)域
RadarMapRadialsValuesPolygon = new Polygon() { Fill = ValuesAreaFill, Stroke = ValuesAreaStroke, Opacity = 0.2 };
InitializeComponent();
}
private void RadarMapUserControl_SizeChanged(object sender, SizeChangedEventArgs e)
{
RefreshRadarMap();
}
/// <summary>
/// 刷新雷達(dá)圖的層的填充色和層線粗細(xì)
/// </summary>
private void RefreshLayersFillBrushAndThickness()
{
//繪制雷達(dá)圖層的多邊形
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>
/// 刷新射線的顏色和粗細(xì)
/// </summary>
private void RefreshRadialBrushAndThinkness()
{
foreach (var item in RadarMapRadialsPolyline)
{
item.Stroke = RadialBrush;
item.StrokeThickness = RadialThickness;
}
}
/// <summary>
/// 刷新雷達(dá)圖
/// </summary>
private void RefreshRadarMap()
{
Grid_RadarMap.Children.Clear();
//首先清除一下polygon里存儲的數(shù)據(jù)
for (int i = 0; i < Layers; i++)
{
RadarMapLayersPolygon[i]?.Points?.Clear();
}
for (int i = 0; i < Radials; i++)
{
RadarMapRadialsPolyline[i]?.Points?.Clear();
}
//如果設(shè)置了LayersPercentList,并且LayersPercentList的元素個(gè)數(shù)與層數(shù)相同則按照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); //計(jì)算每層的默認(rèn)占比
}
}
//計(jì)算每個(gè)扇區(qū)的角度
Angle = 360 / Radials;
//計(jì)算并添加雷達(dá)圖的區(qū)域線和射線上的點(diǎn)
for (int i = 0; i < Radials; i++)
{
//射線上每層的點(diǎn),從外到內(nè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);
//添加到區(qū)域線中
RadarMapLayersPolygon[j].Points.Add(p);
}
//添加到射線中
foreach (var item in points)
{
RadarMapRadialsPolyline[i].Points.Add(item);
}
//計(jì)算原點(diǎn)并添加到射線中
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);
}
//繪制區(qū)域?qū)?
foreach (var polygon in RadarMapLayersPolygon)
{
if (!Grid_RadarMap.Children.Contains(polygon))
Grid_RadarMap.Children.Add(polygon);
}
//繪制雷達(dá)圖射線
foreach (var polyline in RadarMapRadialsPolyline)
{
if (!Grid_RadarMap.Children.Contains(polyline))
Grid_RadarMap.Children.Add(polyline);
}
//繪制雷達(dá)圖上的文字
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>
/// 刷新雷達(dá)圖上值的點(diǎn)的半徑和填充色,以及高亮點(diǎn)的半徑和填充色
/// </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>
/// 刷新雷達(dá)圖值區(qū)域的填充色和邊框色
/// </summary>
private void RefreshValuesAreaBrushAndStroke()
{
RadarMapRadialsValuesPolygon.Fill = ValuesAreaFill;
RadarMapRadialsValuesPolygon.Stroke = ValuesAreaStroke;
}
/// <summary>
/// 刷新射線上的文字標(biāo)題
/// </summary>
/// <param name="point">圖層最外層的點(diǎn)</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;
//計(jì)算文字的實(shí)際像素值
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>
/// 繪制雷達(dá)圖上的點(diǎn)
/// </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>
/// 根據(jù)圓心,擴(kuò)展繪制圓
/// </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>
/// 獲得某一顏色區(qū)間的顏色集合
/// </summary>
/// <param name="sourceColor">起始顏色</param>
/// <param name="destColor">終止顏色</param>
/// <param name="count">分度數(shù)</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
}
}以上就是WPF自定義實(shí)現(xiàn)雷達(dá)圖控件的示例詳解的詳細(xì)內(nèi)容,更多關(guān)于WPF雷達(dá)圖的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
使用revit api畫垂直于風(fēng)管的風(fēng)管示例
這篇文章主要介紹了使用revit api畫垂直于風(fēng)管的風(fēng)管示例,需要的朋友可以參考下2014-03-03
C#對桌面應(yīng)用程序自定義鼠標(biāo)光標(biāo)
這篇文章介紹了C#對桌面應(yīng)用程序自定義鼠標(biāo)光標(biāo)的方法,文中通過示例代碼介紹的非常詳細(xì)。對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-06-06

