WPF-封装自定义雷达图控件
源码地址:https://gitee.com/LiuShuiRuoBing/code_blog
- 当要创建可以Binding的集合时,需要定义IEnumerable的类型,注册时PropertyMetadata要使用FrameworkPropertyMetadata
- 当需要修改属性值,UI会随之更新时,要在注册时,注入PropertyChangedCallback函数
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)));
- 雷达图层的绘制
- Layers 雷达图层数
- LayersPercentList 每一层的占比
- LayerStrokeThickness 每层边框的粗细
- LayerStroke 每层的边框颜色
- InnerColor 雷达图从内到外的渐变色,内部颜色
- OutColor 雷达图从内到外的渐变色,外部颜色
- 雷达图射线
- Radials 雷达图的射线数
- Radius 雷达图半径
- RadialBrush 射线颜色
- RadialThickness 射线粗细
- 雷达图上的点
- Values ,普通点的集合,可以绑定
- ValueRadius 普通点的绘制半径
- ValueBrush 普通点的填充色
- HeightLightValues, 希望高亮点的集合
- HeighLightRadius,高亮点的半径
- HeighLightBrush,高亮点的
- 雷达图上的值区域
- ValuesAreaFill 值区域的填充色
- ValuesAreaStroke 值区域的边框色
- 雷达图的射线标题文字
- ShowTitle 是否显示标题文字
- TitleForground 文字的前景色
- TitleFontSize 文字的字号
- TitleFontWeight 字重
- Titles ,集合, 每条射线要现实的文字
<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
}
}
WPF-封装自定义雷达图控件的更多相关文章
- WPF知识点--自定义Button(ControlTemplate控件模板)
ControlTemplate是一种控件模板,可以通过它自定义一个模板来替换掉控件的默认模板以便打造个性化的控件. ControlTemplate包含两个重要的属性:VisualTree 该模板的视觉 ...
- WPF教程十二:了解自定义控件的基础和自定义无外观控件
这一篇本来想先写风格主题,主题切换.自定义配套的样式.但是最近加班.搬家.新租的房子打扫卫生,我家宝宝6月中旬要出生协调各种的事情,导致了最近精神状态不是很好,又没有看到我比较喜欢的主题风格去模仿的, ...
- WPF自定义选择年月控件详解
本文实例为大家分享了WPF自定义选择年月控件的具体代码,供大家参考,具体内容如下 封装了一个选择年月的控件,XAML代码: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 ...
- WPF自定义控件与样式(8)-ComboBox与自定义多选控件MultComboBox
一.前言 申明:WPF自定义控件与样式是一个系列文章,前后是有些关联的,但大多是按照由简到繁的顺序逐步发布的等,若有不明白的地方可以参考本系列前面的文章,文末附有部分文章链接. 本文主要内容: 下拉选 ...
- [转]Oracle分页之二:自定义web分页控件的封装
本文转自:http://www.cnblogs.com/scy251147/archive/2011/04/16/2018326.html 上节中,讲述的就是Oracle存储过程分页的使用方式,但是如 ...
- 【转】WPF自定义控件与样式(8)-ComboBox与自定义多选控件MultComboBox
一.前言 申明:WPF自定义控件与样式是一个系列文章,前后是有些关联的,但大多是按照由简到繁的顺序逐步发布的等. 本文主要内容: 下拉选择控件ComboBox的自定义样式及扩展: 自定义多选控件Mul ...
- WPF 自定义ComboBox样式,自定义多选控件
原文:WPF 自定义ComboBox样式,自定义多选控件 一.ComboBox基本样式 ComboBox有两种状态,可编辑和不可编辑状态.通过设置IsEditable属性可以切换控件状态. 先看基本样 ...
- 甘特图控件如何自定义绘图?DevExpress Winforms帮你忙
DevExpress Winforms Controls 内置140多个UI控件和库,完美构建流畅.美观且易于使用的应用程序.无论是Office风格的界面,还是分析处理大批量的业务数据,DevExpr ...
- WPF自定义控件(一)の控件分类
一.什么是控件(Controls) 控件是指对数据和方法的封装.控件可以有自己的属性和方法,其中属性是控件数据的简单访问者,方法则是控件的一些简单而可见的功能.控件创建过程包括设计.开发.调试(就是所 ...
- C# Winform 通过FlowLayoutPanel及自定义的编辑控件,实现快速构建C/S版的编辑表单页面
个人理解,开发应用程序的目的,不论是B/S或是C/S结构类型,无非就是实现可供用户进行查.增.改.删,其中查询用到最多,开发设计的场景也最为复杂,包括但不限于:表格记录查询.报表查询.导出文件查询等等 ...
随机推荐
- dates()datetimes()查询都有哪些日期
dates()查询都有哪些日期 created_at是列名,year,是要查询的参数,order为排序方式 Course.objects.dates('created_at','year',order ...
- ESlint配置详解
开发中出现eslint提示代码格式错误,有时候不明白其配置规范,是件很头疼的事情到处找api又是半天:so记录一份配置详情便于开发中翻阅 { // 环境定义了预定义的全局变量. "env&q ...
- windows内核学习一
变量类型 kernel user ULONG unsigned long PULONG unsigned long* UCHAR unsigned char PUCHAR unsigned char* ...
- 文件系统考古2:1984 - BSD Fast Filing System
今天继续与大家分享系列文章<50 years in filesystems>,由 KRISTIAN KÖHNTOPP 撰写. 我们将进入文件系统的第二个十年,即1984年,计算机由微型计算 ...
- Python3.9+torch1.7.1+cuda11.0+cudnn8.0+Anaconda3安装
前言想要安装pytorch,至少得先安装Anaconda.python!!!必要的不想用cpu要用gpu的还需要cuda11.0+cudnn11.0!!!一.安装python3.9二.安装Anacon ...
- CSS3学习记录之loading动画
loading动画就是在加载一些网页内容的时候呈现出来的小动画,记录一下学到的几种loading动画: 效果:http://39.105.101.122/myhtml/CSS/Loading/load ...
- ProtocolBuffers的国际化和本地化支持
目录 1. 引言 2. 技术原理及概念 3. 实现步骤与流程 4. 应用示例与代码实现讲解 5. 优化与改进 34.< Protocol Buffers 的国际化和本地化支持> 本文将介绍 ...
- Unity的OnOpenAsset:深入解析与实用案例
Unity OnOpenAsset 在Unity中,OnOpenAsset是一个非常有用的回调函数,它可以在用户双击资源文件时自动打开一个编辑器窗口.这个回调函数可以用于自定义资源编辑,提高工作效率. ...
- 【Spring boot】 @Value注解
一.不通过配置文件的注入属性 1.1 注入普通字符串 直接附在属性名上,在 Bean 初始化时,会赋初始值 @Value("normal") private String norm ...
- @Import :Spring Bean模块装配的艺术
本文分享自华为云社区<Spring高手之路8--Spring Bean模块装配的艺术:@Import详解>,作者: 砖业洋__. 本文将带你深入探索Spring框架的装配机制,以及它如何使 ...