当前位置: 移动技术网 > IT编程>开发语言>c# > WPF 自定义雷达图开发实例教程

WPF 自定义雷达图开发实例教程

2019年07月18日  | 移动技术网IT编程  | 我要评论
自定义雷达图表如下: 1、创建usercontrol,名为“radarchartcontrol” 前台: <usercontrol x:class=

自定义雷达图表如下:

1、创建usercontrol,名为“radarchartcontrol”

前台:

<usercontrol x:class="wpfapplication2.radarchartcontrol" 
 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" 
 mc:ignorable="d" 
 d:designheight="300" d:designwidth="300" loaded="radarchartcontrol_onloaded">
 <canvas x:name="canvaspanel" horizontalalignment="center" verticalalignment="center">
 </canvas>
</usercontrol>

后台:

/// <summary>
 /// radarchartcontrol.xaml 的交互逻辑
 /// </summary>
 public partial class radarchartcontrol : usercontrol
 {
 public radarchartcontrol()
 {
  initializecomponent();
 }
 #region 属性
 /// <summary>
 /// 尺寸大小
 /// 高宽大小一样
 /// </summary>
 public double size
 {
  get { return (double)getvalue(sizeproperty); }
  set { setvalue(sizeproperty, value); }
 }
 public static readonly dependencyproperty sizeproperty = dependencyproperty.register("size", typeof(double),
 typeof(radarchartcontrol), new propertymetadata(400.0));
 /// <summary>
 /// 标题
 /// </summary>
 public list<argumentmodel> arguments
 {
  get { return (list<argumentmodel>)getvalue(argumentsproperty); }
  set { setvalue(argumentsproperty, value); }
 }
 public static readonly dependencyproperty argumentsproperty = dependencyproperty.register("arguments", typeof(list<argumentmodel>),
 typeof(radarchartcontrol), new propertymetadata(new list<argumentmodel>()));
 /// <summary>
 /// 数据
 /// </summary>
 public list<chartitem> datas
 {
  get { return (list<chartitem>)getvalue(datasproperty); }
  set { setvalue(datasproperty, value); }
 }
 public static readonly dependencyproperty datasproperty = dependencyproperty.register("datas", typeof(list<chartitem>),
 typeof(radarchartcontrol), new propertymetadata(new list<chartitem>()));
 /// <summary>
 /// 获取或设置线条颜色
 /// </summary>
 public brush borderbrush
 {
  get { return (brush)getvalue(borderbrushproperty); }
  set { setvalue(borderbrushproperty, value); }
 }
 public static readonly dependencyproperty borderbrushproperty = dependencyproperty.register("borderbrush", typeof(brush),
 typeof(radarchartcontrol), new propertymetadata(brushes.royalblue));
 /// <summary>
 /// 连接点大小
 /// </summary>
 public int ellipsesize = 7;
 /// <summary>
 /// 控件大小
 /// </summary>
 public double totalsize
 {
  get
  {
  double size = size + 200;
  return size;
  }
 }
 /// <summary>
 /// 面板
 /// </summary>
 public canvas chartcanvas = new canvas();
 //声明和注册路由事件
 public static readonly routedevent titleclickroutedevent =
 eventmanager.registerroutedevent("titleclick", routingstrategy.bubble, typeof(eventhandler<routedeventargs>), typeof(radarchartcontrol));
 //clr事件包装
 public event routedeventhandler titleclick
 {
  add { this.addhandler(titleclickroutedevent, value); }
  remove { this.removehandler(titleclickroutedevent, value); }
 }
 //激发路由事件,借用click事件的激发方法
 protected void onclick(object sender, routedeventargs e)
 {
  routedeventargs args = new routedeventargs(titleclickroutedevent, e);
  this.raiseevent(args);
 }
 #endregion
 private void radarchartcontrol_onloaded(object sender, routedeventargs e)
 {
  if (!checkdata())
  {
  throw new exception("radarchart的数据之间不匹配!请重新配置!");
  }
  //获取最大数值
  int maxdata = datas.max(i => i.datalist.max(o => o.data));
  //设置面板和背景
  setcanvasandbackground(maxdata);
  //设置数据标题
  setdatatitle(datas);
  //获取半圈大小
  double length = size / 2 / maxdata;
  //连接点半径
  int ellipser = ellipsesize / 2;
  foreach (var chartitem in datas)
  {
  var color = chartitem.color;
  //俩个多边形,一个设置背景,一个设置边框
  polygon polygonarea = new polygon() { fill = color, opacity = 0.2, strokethickness = 0 };
  polygon polygonborder = new polygon() { fill = brushes.transparent, stroke = color, strokethickness = 0.8 };
  int index = 0;
  foreach (var data in chartitem.datalist)
  {
   double currentangle = angle * index + 90;
   double angle = (currentangle / 360) * 2 * math.pi;
   var r = data.data * length;
   double x = size / 2 + r * math.cos(angle);
   double y = size / 2 - r * math.sin(angle);
   //多边形添加节点
   var point = new point()
   {
   x = x,
   y = y
   };
   polygonarea.points.add(point);
   polygonborder.points.add(point);
   //设置节点style
   var ellipse = new ellipse() { width = ellipsesize, height = ellipsesize, fill = color };
   canvas.setleft(ellipse, x - ellipser);
   canvas.settop(ellipse, y - ellipser);
   chartcanvas.children.add(ellipse);
   index++;
  }
  chartcanvas.children.add(polygonarea);
  chartcanvas.children.add(polygonborder);
  }
  //设置标题
  setarguments();
 }
 /// <summary>
 /// 设置数据标题
 /// </summary>
 /// <param name="datas"></param>
 private void setdatatitle(list<chartitem> datas)
 {
  radarcharttitlelist titlelist = new radarcharttitlelist();
  titlelist.itemsoure = datas;
  double angle = math.pi * 0.25;
  double x = totalsize / 2 + (totalsize / 2) * math.sin(angle);
  canvas.setleft(titlelist, x);
  canvas.settop(titlelist, x);
  canvaspanel.children.add(titlelist);
 }
 /// <summary>
 /// 设置标题
 /// </summary>
 private void setarguments()
 {
  int index = 0;
  foreach (var argument in arguments)
  {
  var button = new chartbutton();
  button.content = argument.name;
  button.icon = argument.iconsource;
  button.mybutton.click += onclick;
  //绘制xy
  double currentangle = angle * index + 90;
  double angle = (currentangle / 360) * 2 * math.pi;
  var r = totalsize / 2;
  double x = r + r * math.cos(angle) - (button.width / 2);
  double y = r - r * math.sin(angle) - (button.height / 2);
  //添加按钮高度差异
  y = y + math.sin(angle) * (button.width / 2 - button.height / 2);
  canvas.setleft(button, x);
  canvas.settop(button, y);
  canvaspanel.children.add(button);
  index++;
  }
 }
 /// <summary>
 /// 检查数据
 /// </summary>
 /// <returns></returns>
 private bool checkdata()
 {
  if (datas == null)
  {
  return false;
  }
  foreach (var data in datas)
  {
  bool result = !datas.any(i => i.datalist.count != data.datalist.count);
  if (!result)
  {
   return false;
  }
  }
  return true;
 }
 /// <summary>
 /// 设置面板和背景
 /// </summary>
 /// <param name="maxindex"></param>
 private void setcanvasandbackground(int maxindex)
 {
  canvaspanel.height = totalsize;
  canvaspanel.width = totalsize;
  //面板
  chartcanvas.height = size;
  chartcanvas.width = size;
  double canvasx = (totalsize - size) / 2;
  canvas.setleft(chartcanvas, canvasx);
  canvas.settop(chartcanvas, canvasx);
  canvaspanel.children.add(chartcanvas);
  //画圈和直线
  var color = borderbrush;
  double length = size / 2 / maxindex;
  for (int i = 0; i < maxindex; i++)
  {
  double height = length * 2 * (i + 1);
  double left = size / 2 - length * (i + 1);
  var ellipse = new ellipse() { stroke = color, strokethickness = 0.5, height = height, width = height };
  canvas.setleft(ellipse, left);
  canvas.settop(ellipse, left);
  chartcanvas.children.add(ellipse);
  }
  //暂时设定:4个标题时,画线
  if (arguments.count == 4)
  {
  //竖向直线
  path verticalpath = new path()
  {
   stroke = color,
   strokethickness = 0.2,
  };
  //添加数据
  streamgeometry geometry = new streamgeometry();
  geometry.fillrule = fillrule.nonzero; //声前f0还是f1,现在是f1
  using (streamgeometrycontext ctx = geometry.open())
  {
   ctx.beginfigure(new point(size / 2, 0), true, true);
   ctx.lineto(new point(size / 2, size), true, false);
  }
  geometry.freeze();
  verticalpath.data = geometry;
  chartcanvas.children.add(verticalpath);
  //横向直线
  path horizontalpath = new path()
  {
   stroke = color,
   strokethickness = 0.2,
  };
  //添加数据
  geometry = new streamgeometry();
  geometry.fillrule = fillrule.nonzero; //声前f0还是f1,现在是f1
  using (streamgeometrycontext ctx = geometry.open())
  {
   ctx.beginfigure(new point(0, size / 2), true, true);
   ctx.lineto(new point(size, size / 2), true, false);
  }
  geometry.freeze();
  horizontalpath.data = geometry;
  chartcanvas.children.add(horizontalpath);
  }
 }
 /// <summary>
 /// 分隔角度
 /// </summary>
 private double angle
 {
  get
  {
  int count = arguments.count;
  double angle = 360 / count;
  return angle;
  }
 }
 }
 /// <summary>
 /// 类标题
 /// </summary>
 public class argumentmodel
 {
 public imagesource iconsource { get; set; }
 public string name { get; set; }
 }
 /// <summary>
 /// 单组数据
 /// </summary>
 public class chartitem
 {
 public brush color { get; set; }
 list<chartdata> datalist = new list<chartdata>();
 public list<chartdata> datalist
 {
  get { return datalist; }
  set { datalist = value; }
 }
 public object name { get; set; }
 }
 /// <summary>
 /// 数据
 /// </summary>
 public class chartdata
 {
 public string name { get; set; }
 public int data { get; set; }
 }

2、创建标题类按钮控件,定义名称为chartbutton

前台:

<usercontrol x:class="wpfapplication2.chartbutton"
  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" 
  mc:ignorable="d"
  d:designheight="80" d:designwidth="200" loaded="chartbutton_onloaded">
 <usercontrol.resources>
 <style targettype="button">
  <setter property="foreground" value="white"></setter>
 </style>
 </usercontrol.resources>
 <grid>
 <button x:name="mybutton" verticalalignment="center" horizontalalignment="center">
  <button.template>
  <controltemplate targettype="{x:type button}">
   <grid x:name="buttongrid" height="{templatebinding height}">
   <rectangle x:name="buttonretc" radiusx="20" radiusy="25" stroke="#ff06ffe8"></rectangle>
   <stackpanel orientation="horizontal" margin="20,5" horizontalalignment="center">
    <rectangle height="{binding iconheight}" width="{binding iconwidth}">
    <rectangle.fill>
     <imagebrush imagesource="{binding icon}"></imagebrush>
    </rectangle.fill>
    </rectangle>
    <textblock x:name="buttontextblock" text="{templatebinding content}" foreground="{templatebinding foreground}" margin="8,-2,0,0" fontsize="22" verticalalignment="center" textalignment="center"></textblock>
   </stackpanel>
   </grid>
   <controltemplate.triggers>
   <datatrigger binding="{binding elementname=mybutton,path=isfocused}" value="true">
    <datatrigger.setters>
    <setter targetname="buttonretc" property="fill" value="#ffa9bcff"></setter>
    <setter targetname="buttonretc" property="strokethickness" value="0.5"></setter>
    <setter targetname="buttontextblock" property="foreground" value="#ff06ffe8"></setter>
    </datatrigger.setters>
   </datatrigger>
   <datatrigger binding="{binding elementname=mybutton,path=ispressed}" value="true">
    <datatrigger.setters>
    <setter targetname="buttontextblock" property="fontweight" value="bold"></setter>
    </datatrigger.setters>
   </datatrigger>
   <datatrigger binding="{binding elementname=mybutton,path=isfocused}" value="false">
    <datatrigger.setters>
    <setter targetname="buttonretc" property="fill" value="transparent"></setter>
    <setter targetname="buttonretc" property="strokethickness" value="0"></setter>
    </datatrigger.setters>
   </datatrigger>
   </controltemplate.triggers>
  </controltemplate>
  </button.template>
 </button>
 </grid>
</usercontrol>

后台:

/// <summary>
 /// chartbutton.xaml 的交互逻辑
 /// </summary>
 public partial class chartbutton : usercontrol
 {
 public chartbutton()
 {
  initializecomponent();
 }
 #region 属性
 /// <summary>
 /// 工具提示
 /// </summary>
 public string tooltip
 {
  get { return (string)getvalue(tooltipproperty); }
  set { setvalue(tooltipproperty, value); }
 }
 public static readonly dependencyproperty tooltipproperty = dependencyproperty.register("tooltip",
 typeof(string), typeof(chartbutton), new propertymetadata());
 /// <summary>
 /// 按钮内容
 /// </summary>
 public string content
 {
  get { return (string)getvalue(contentproperty); }
  set { setvalue(contentproperty, value); }
 }
 public static readonly dependencyproperty contentproperty = dependencyproperty.register("content",
 typeof(string), typeof(chartbutton), new propertymetadata("按钮"));
 /// <summary>
 /// 图标
 /// </summary>
 public imagesource icon
 {
  get { return (imagesource)getvalue(iconproperty); }
  set { setvalue(iconproperty, value); }
 }
 public static readonly dependencyproperty iconproperty = dependencyproperty.register("icon",
 typeof(imagesource), typeof(chartbutton), new propertymetadata());
 /// <summary>
 /// 图标高度
 /// </summary>
 public double iconheight
 {
  get { return (double)getvalue(iconheightproperty); }
  set { setvalue(iconheightproperty, value); }
 }
 public static readonly dependencyproperty iconheightproperty = dependencyproperty.register("iconheight",
 typeof(double), typeof(chartbutton), new propertymetadata(25.0));
 /// <summary>
 /// 图标宽度
 /// </summary>
 public double iconwidth
 {
  get { return (double)getvalue(iconwidthproperty); }
  set { setvalue(iconwidthproperty, value); }
 }
 public static readonly dependencyproperty iconwidthproperty = dependencyproperty.register("iconwidth",
 typeof(double), typeof(chartbutton), new propertymetadata(25.0));
 /// <summary>
 /// 高度
 /// </summary>
 public double height
 {
  get { return (double)getvalue(heightproperty); }
  set { setvalue(heightproperty, value); }
 }
 public static readonly dependencyproperty heightproperty = dependencyproperty.register("height",
 typeof(double), typeof(chartbutton), new propertymetadata(46.0));
 /// <summary>
 /// 宽度
 /// </summary>
 public double width
 {
  get { return (double)getvalue(widthproperty); }
  set { setvalue(widthproperty, value); }
 }
 public static readonly dependencyproperty widthproperty = dependencyproperty.register("width",
 typeof(double), typeof(chartbutton), new propertymetadata(170.0));
 #endregion
 private void chartbutton_onloaded(object sender, routedeventargs e)
 {
  mybutton.tooltip = tooltip;
  mybutton.content = content;
  mybutton.width = width;
  mybutton.height = height;
  if (icon != null)
  {
  mybutton.datacontext = new chartbuttonmodel()
  {
   icon = icon,
   iconheight = iconheight,
   iconwidth = iconwidth
  };
  }
 }
 }
 public class chartbuttonmodel
 {
 public imagesource icon { get; set; }
 public double iconheight { get; set; }
 public double iconwidth { get; set; }
 }

3、定义数据组标题显示列表

前台:

<usercontrol x:class="wpfapplication2.radarcharttitlelist"
  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" 
  mc:ignorable="d" 
  d:designheight="300" d:designwidth="300" loaded="radarcharttitlelist_onloaded">
 <usercontrol.resources>
 <style x:key="itemcontainer" targettype="{x:type listboxitem}">
  <setter property="template">
  <setter.value>
   <controltemplate targettype="{x:type listboxitem}">
   <border x:name="iconborder" background="transparent" cornerradius="4" borderthickness="0">
    <contentpresenter />
   </border>
   <controltemplate.triggers>
    <trigger property="isselected" value="true">
    <setter targetname="iconborder" property="bitmapeffect">
     <setter.value>
     <outerglowbitmapeffect glowcolor="transparent" glowsize="5" />
     </setter.value>
    </setter>
    </trigger>
   </controltemplate.triggers>
   </controltemplate>
  </setter.value>
  </setter>
 </style>
 </usercontrol.resources>
 <grid>
 <listbox x:name="mylistbox" itemssource="{binding}" itemcontainerstyle="{staticresource itemcontainer}" focusvisualstyle="{x:null}">
  <listbox.template>
  <controltemplate>
   <stackpanel background="transparent" isitemshost="true"></stackpanel>
  </controltemplate>
  </listbox.template>
  <listbox.itemtemplate>
  <datatemplate>
   <grid horizontalalignment="left" verticalalignment="center" background="transparent">
   <grid.columndefinitions>
    <columndefinition width="auto"></columndefinition>
    <columndefinition width="*"></columndefinition>
   </grid.columndefinitions>
   <grid horizontalalignment="center" margin="10,0" background="transparent">
    <ellipse fill="{binding color}" height="6" width="6" horizontalalignment="right" verticalalignment="center"></ellipse>
    <canvas verticalalignment="center" horizontalalignment="center">
    <path fill="{binding color}" height="5" strokethickness="1" stroke="{binding color}" verticalalignment="center" data="m-10,0 l10,0"></path>
    </canvas>
   </grid>
   <textblock grid.column="1" text="{binding name}" foreground="white" background="transparent"></textblock>
   </grid>
  </datatemplate>
  </listbox.itemtemplate>
 </listbox>
 </grid>
</usercontrol>

后台:

/// <summary>
 /// radarcharttitlelist.xaml 的交互逻辑
 /// </summary>
 public partial class radarcharttitlelist : usercontrol
 {
 public radarcharttitlelist()
 {
  initializecomponent();
 }
 /// <summary>
 /// 数据
 /// </summary>
 public list<chartitem> itemsoure
 {
  get { return (list<chartitem>)getvalue(itemsoureproperty); }
  set { setvalue(itemsoureproperty, value); }
 }
 public static readonly dependencyproperty itemsoureproperty = dependencyproperty.register("itemsoure", typeof(list<chartitem>),
 typeof(radarchartcontrol), new propertymetadata(new list<chartitem>()));
 private void radarcharttitlelist_onloaded(object sender, routedeventargs e)
 {
  this.datacontext = itemsoure;
 }
 }

4、界面引用控件

<window x:class="wpfapplication2.mainwindow"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:wpfapplication2="clr-namespace:wpfapplication2"
  title="mainwindow" height="350" width="525" background="lightgray">
 <grid>
  <wpfapplication2:radarchartcontrol x:name="radarchartcontrol" horizontalalignment="center" verticalalignment="center">
   <wpfapplication2:radarchartcontrol.arguments>
    <wpfapplication2:argumentmodel name="c#" iconsource="chart_bar_big.png"></wpfapplication2:argumentmodel>
    <wpfapplication2:argumentmodel name="java" iconsource="blueprint_blog.png"></wpfapplication2:argumentmodel>
    <wpfapplication2:argumentmodel name="python" iconsource="chart_graph_descending.png"></wpfapplication2:argumentmodel>
    <wpfapplication2:argumentmodel name="vb" iconsource="chart_bar_big.png"></wpfapplication2:argumentmodel>
    <wpfapplication2:argumentmodel name="其它" iconsource="chart_graph_descending.png"></wpfapplication2:argumentmodel>
   </wpfapplication2:radarchartcontrol.arguments>
   <wpfapplication2:radarchartcontrol.datas>
    <wpfapplication2:chartitem name="应聘者a" color="#ff07c507">
     <wpfapplication2:chartitem.datalist>
      <wpfapplication2:chartdata data="1"></wpfapplication2:chartdata>
      <wpfapplication2:chartdata data="3"></wpfapplication2:chartdata>
      <wpfapplication2:chartdata data="3"></wpfapplication2:chartdata>
      <wpfapplication2:chartdata data="4"></wpfapplication2:chartdata>
      <wpfapplication2:chartdata data="4"></wpfapplication2:chartdata>
     </wpfapplication2:chartitem.datalist>
    </wpfapplication2:chartitem>
    <wpfapplication2:chartitem name="应聘者b" color="#ff508bf3">
     <wpfapplication2:chartitem.datalist>
      <wpfapplication2:chartdata data="4"></wpfapplication2:chartdata>
      <wpfapplication2:chartdata data="1"></wpfapplication2:chartdata>
      <wpfapplication2:chartdata data="2"></wpfapplication2:chartdata>
      <wpfapplication2:chartdata data="1"></wpfapplication2:chartdata>
      <wpfapplication2:chartdata data="1"></wpfapplication2:chartdata>
     </wpfapplication2:chartitem.datalist>
    </wpfapplication2:chartitem>
    <wpfapplication2:chartitem name="应聘者c" color="#fff73131">
     <wpfapplication2:chartitem.datalist>
      <wpfapplication2:chartdata data="2"></wpfapplication2:chartdata>
      <wpfapplication2:chartdata data="2"></wpfapplication2:chartdata>
      <wpfapplication2:chartdata data="3"></wpfapplication2:chartdata>
      <wpfapplication2:chartdata data="3"></wpfapplication2:chartdata>
      <wpfapplication2:chartdata data="3"></wpfapplication2:chartdata>
     </wpfapplication2:chartitem.datalist>
    </wpfapplication2:chartitem>
   </wpfapplication2:radarchartcontrol.datas>
  </wpfapplication2:radarchartcontrol>
 </grid>
</window>

以上所述是小编给大家介绍的wpf 自定义雷达图开发实例教程,希望对大家有所帮助

如您对本文有疑问或者有任何想说的,请点击进行留言回复,万千网友为您解惑!

相关文章:

验证码:
移动技术网