WPF 自定义雷达图开发实例教程
程序员文章站
2022-11-01 14:08:59
自定义雷达图表如下:
1、创建usercontrol,名为“radarchartcontrol”
前台:
自定义雷达图表如下:
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 自定义雷达图开发实例教程,希望对大家有所帮助
下一篇: PHP+MYSQL的文章管理系统(二)