325人参与 • 2024-10-21 • Windows Phone
wpf 实现饼状统计图
.net4 至 .net8;visual studio 2022;chartpie 详解
新增依赖属性 datas 存储饼图的数据,当数据发生更改时触发控件的重绘。
构造初始化颜色组 (vibrantcolors) 为了区分每个扇形区显示不同的颜色。
绘制饼图
var drawingpen = createpen(2); var bolddrawingpen = createpen(4); var piewidth = actualwidth > actualheight ? actualheight : actualwidth; var pieheight = actualwidth > actualheight ? actualheight : actualwidth; centerx = piewidth / 2; centery = pieheight / 2; radius = actualwidth > actualheight ? actualheight / 2 : actualwidth / 2;
绘制每个扇形
var angle = 0d;
var prevangle = 0d;
var sum = datas.select(ser => ser.value).sum();
var index = 0;
var isfirst = false;
foreach (var item in datas)
{
// 计算起始和结束角度
var arcstartx = radius * math.cos(angle * math.pi / 180) + centerx;
var arcstarty = radius * math.sin(angle * math.pi / 180) + centery;
angle = item.value / sum * 360 + prevangle;
var arcendx = 0d;
var arcendy = 0d;
if (datas.count() == 1 && angle == 360)
{
isfirst = true;
arcendx = centerx + math.cos(359.99999 * math.pi / 180) * radius;
arcendy = radius * math.sin(359.99999 * math.pi / 180) + centery;
}
else
{
arcendx = centerx + math.cos(angle * math.pi / 180) * radius;
arcendy = radius * math.sin(angle * math.pi / 180) + centery;
}
var startpoint = new point(arcstartx, arcstarty);
var line1segment = new linesegment(startpoint, false);
var islargearc = item.value / sum > 0.5;
var arcsegment = new arcsegment
{
size = new size(radius, radius),
point = new point(arcendx, arcendy),
sweepdirection = sweepdirection.clockwise,
islargearc = islargearc
};
var center = new point(centerx, centery);
var line2segment = new linesegment(center, false);
var pathgeometry = new pathgeometry(new[]
{
new pathfigure(center, new list<pathsegment>
{
line1segment,
arcsegment,
line2segment
}, true)
});
pathgeometries.add(pathgeometry,
$"{item.key} : {item.value.formatnumber()}");
var backgroupbrush = new solidcolorbrush
{
color = vibrantcolors[
index >= vibrantcolors.length
? index % vibrantcolors.length
: index]
};
backgroupbrush.freeze();
drawingcontext.drawgeometry(backgroupbrush, null, pathgeometry);
index++;
if (!isfirst)
{
if (index == 1)
drawingcontext.drawline(bolddrawingpen, center, startpoint);
else
drawingcontext.drawline(drawingpen, center, startpoint);
}
prevangle = angle;
}
angle 和 prevangle,计算数据总和(sum)。datas 集合,计算每条数据所需占的扇形区的起始角度和结束的角度。360度,然后绘制圆形。arcsegment 绘制圆形的弧度,连接圆心和扇形区边缘。pathgeometry 添加到 pathgeometries 中,并绘制每个的扇形区。prevangle 以用于计算下一个扇形区的角度。1)新增 chartpie.cs 代码如下:
using system;
using system.collections.generic;
using system.linq;
using system.windows;
using system.windows.controls;
using system.windows.controls.primitives;
using system.windows.input;
using system.windows.media;
using system.windows.media.effects;
using system.windows.shapes;
using wpfdevelopers.core;
namespace wpfdevelopers.controls
{
public class chartpie : control
{
public static readonly dependencyproperty datasproperty =
dependencyproperty.register("datas", typeof(ienumerable<keyvaluepair<string, double>>),
typeof(chartpie), new uipropertymetadata(dataschanged));
private border _border;
private ellipse _ellipse;
private keyvaluepair<pathgeometry, string> _lastitem;
private popup _popup;
private stackpanel _stackpanel;
private textblock _textblock;
private double centerx, centery, radius;
private bool ispopupopen;
private readonly dictionary<pathgeometry, string> pathgeometries = new dictionary<pathgeometry, string>();
private readonly color[] vibrantcolors;
public chartpie()
{
vibrantcolors = new[]
{
color.fromargb(255, 84, 112, 198),
color.fromargb(255, 145, 204, 117),
color.fromargb(255, 250, 200, 88),
color.fromargb(255, 238, 102, 102),
color.fromargb(255, 115, 192, 222),
color.fromargb(255, 59, 162, 114),
color.fromargb(255, 252, 132, 82),
color.fromargb(255, 154, 96, 180),
color.fromargb(255, 234, 124, 204)
};
}
public ienumerable<keyvaluepair<string, double>> datas
{
get => (ienumerable<keyvaluepair<string, double>>) getvalue(datasproperty);
set => setvalue(datasproperty, value);
}
private static void dataschanged(dependencyobject d, dependencypropertychangedeventargs e)
{
var ctrl = d as chartpie;
if (e.newvalue != null)
ctrl.invalidatevisual();
}
protected override void onmousemove(mouseeventargs e)
{
base.onmousemove(e);
if (datas == null || datas.count() == 0 || ispopupopen) return;
if (_popup == null)
{
_popup = new popup
{
allowstransparency = true,
placement = placementmode.mousepoint,
placementtarget = this,
staysopen = false
};
_popup.mousemove += (y, j) =>
{
var point = j.getposition(this);
if (ispopupopen && _lastitem.value != null)
if (!ismouseovergeometry(_lastitem.key))
{
_popup.isopen = false;
ispopupopen = false;
_lastitem = new keyvaluepair<pathgeometry, string>();
}
};
_popup.closed += delegate { ispopupopen = false; };
_textblock = new textblock
{
horizontalalignment = horizontalalignment.center,
verticalalignment = verticalalignment.center,
foreground = (brush) application.current.tryfindresource("wd.windowforegroundcolorbrush"),
padding = new thickness(4, 0, 2, 0)
};
_ellipse = new ellipse
{
width = 10,
height = 10,
stroke = brushes.white
};
_stackpanel = new stackpanel {orientation = orientation.horizontal};
_stackpanel.children.add(_ellipse);
_stackpanel.children.add(_textblock);
_border = new border
{
child = _stackpanel,
background = (brush) application.current.tryfindresource("wd.chartfillsolidcolorbrush"),
effect = application.current.tryfindresource("wd.popupshadowdepth") as dropshadoweffect,
margin = new thickness(10),
cornerradius = new cornerradius(3),
padding = new thickness(6)
};
_popup.child = _border;
}
var index = 0;
foreach (var pathgeometry in pathgeometries)
{
if (ismouseovergeometry(pathgeometry.key))
{
ispopupopen = true;
_ellipse.fill = new solidcolorbrush
{
color = vibrantcolors[index >= vibrantcolors.length ? index % vibrantcolors.length : index]
};
_textblock.text = pathgeometry.value;
_popup.isopen = true;
_lastitem = pathgeometry;
break;
}
index++;
}
}
private bool ismouseovergeometry(pathgeometry pathgeometry)
{
var mouseposition = mouse.getposition(this);
return pathgeometry.fillcontains(mouseposition);
}
protected override void onrender(drawingcontext drawingcontext)
{
base.onrender(drawingcontext);
if (datas == null || datas.count() == 0)
return;
snapstodevicepixels = true;
uselayoutrounding = true;
pathgeometries.clear();
var drawingpen = createpen(2);
var bolddrawingpen = createpen(4);
var piewidth = actualwidth > actualheight ? actualheight : actualwidth;
var pieheight = actualwidth > actualheight ? actualheight : actualwidth;
centerx = piewidth / 2;
centery = pieheight / 2;
radius = actualwidth > actualheight ? actualheight / 2 : actualwidth / 2;
var angle = 0d;
var prevangle = 0d;
var sum = datas.select(ser => ser.value).sum();
var index = 0;
var isfirst = false;
foreach (var item in datas)
{
var arcstartx = radius * math.cos(angle * math.pi / 180) + centerx;
var arcstarty = radius * math.sin(angle * math.pi / 180) + centery;
angle = item.value / sum * 360 + prevangle;
var arcendx = 0d;
var arcendy = 0d;
if (datas.count() == 1 && angle == 360)
{
isfirst = true;
arcendx = centerx + math.cos(359.99999 * math.pi / 180) * radius;
arcendy = radius * math.sin(359.99999 * math.pi / 180) + centery;
}
else
{
arcendx = centerx + math.cos(angle * math.pi / 180) * radius;
arcendy = radius * math.sin(angle * math.pi / 180) + centery;
}
var startpoint = new point(arcstartx, arcstarty);
var line1segment = new linesegment(startpoint, false);
var islargearc = item.value / sum > 0.5;
var arcsegment = new arcsegment();
var size = new size(radius, radius);
var endpoint = new point(arcendx, arcendy);
arcsegment.size = size;
arcsegment.point = endpoint;
arcsegment.sweepdirection = sweepdirection.clockwise;
arcsegment.islargearc = islargearc;
var center = new point(centerx, centery);
var line2segment = new linesegment(center, false);
var pathgeometry = new pathgeometry(new[]
{
new pathfigure(new point(centerx, centery), new list<pathsegment>
{
line1segment,
arcsegment,
line2segment
}, true)
});
pathgeometries.add(pathgeometry,
$"{item.key} : {item.value.formatnumber()}");
var backgroupbrush = new solidcolorbrush
{
color = vibrantcolors[
index >= vibrantcolors.length
? index % vibrantcolors.length
: index]
};
backgroupbrush.freeze();
drawingcontext.drawgeometry(backgroupbrush, null, pathgeometry);
index++;
if (!isfirst)
{
if (index == 1)
drawingcontext.drawline(bolddrawingpen, center, startpoint);
else
drawingcontext.drawline(drawingpen, center, startpoint);
}
prevangle = angle;
}
}
private pen createpen(double thickness)
{
var pen = new pen
{
thickness = thickness,
brush = brushes.white
};
pen.freeze();
return pen;
}
}
}
2)新增 chartpieexample.xaml 示例代码如下:
<grid background="{dynamicresource wd.backgroundsolidcolorbrush}">
<grid.rowdefinitions>
<rowdefinition />
<rowdefinition height="auto" />
</grid.rowdefinitions>
<scrollviewer horizontalscrollbarvisibility="auto" verticalscrollbarvisibility="auto">
<border
height="300"
margin="30,0"
background="{dynamicresource wd.backgroundsolidcolorbrush}">
<wd:chartpie datas="{binding datas, relativesource={relativesource ancestortype=local:chartpieexample}}" />
</border>
</scrollviewer>
<button
grid.row="1"
width="200"
verticalalignment="bottom"
click="button_click"
content="刷新"
style="{staticresource wd.primarybutton}" />
</grid>
3)新增 chartpieexample.xaml.cs 示例代码如下:
using system.collections.generic;
using system.linq;
using system.windows;
using system.windows.controls;
namespace wpfdevelopers.samples.exampleviews
{
/// <summary>
/// chartpieexample.xaml 的交互逻辑
/// </summary>
public partial class chartpieexample : usercontrol
{
public ienumerable<keyvaluepair<string, double>> datas
{
get { return (ienumerable<keyvaluepair<string, double>>)getvalue(datasproperty); }
set { setvalue(datasproperty, value); }
}
public static readonly dependencyproperty datasproperty =
dependencyproperty.register("datas", typeof(ienumerable<keyvaluepair<string, double>>), typeof(chartpieexample), new propertymetadata(null));
private dictionary<string, ienumerable<keyvaluepair<string, double>>> keyvalues = new dictionary<string, ienumerable<keyvaluepair<string, double>>>();
private int _index = 0;
public chartpieexample()
{
initializecomponent();
var models1 = new[]
{
new keyvaluepair<string, double>("mon", 120),
new keyvaluepair<string, double>("tue", 530),
new keyvaluepair<string, double>("wed", 1060),
new keyvaluepair<string, double>("thu", 140),
new keyvaluepair<string, double>("fri", 8000.123456) ,
new keyvaluepair<string, double>("sat", 200) ,
new keyvaluepair<string, double>("sun", 300) ,
};
var models2 = new[]
{
new keyvaluepair<string, double>("bing", 120),
new keyvaluepair<string, double>("google", 170),
new keyvaluepair<string, double>("baidu", 30),
new keyvaluepair<string, double>("github", 200),
new keyvaluepair<string, double>("stack overflow", 100) ,
new keyvaluepair<string, double>("runoob", 180) ,
new keyvaluepair<string, double>("open ai", 90) ,
new keyvaluepair<string, double>("open ai2", 93) ,
new keyvaluepair<string, double>("open ai3", 94) ,
new keyvaluepair<string, double>("open ai4", 95) ,
};
keyvalues.add("1", models1);
keyvalues.add("2", models2);
datas = models1;
}
private void button_click(object sender, routedeventargs e)
{
_index++;
if (_index >= keyvalues.count)
{
_index = 0;
}
datas = keyvalues.tolist()[_index].value;
}
}
}
效果图

以上就是wpf实现绘制饼状统计图的示例代码的详细内容,更多关于wpf绘制饼状统计图的资料请关注代码网其它相关文章!
您想发表意见!!点此发布评论
版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。
发表评论