WPF在Canvas中绘图实现折线统计图
来源:互联网 发布:数据库设计物理设计 编辑:程序博客网 时间:2024/06/09 01:57
最近在WPF中做一个需要实现统计的功能,其中需要用到统计图,之前也没有接触过,度娘上大多都是各种收费或者免费的第三方控件,不想用第三方控件那就自己画一个吧。
在园子还找到一篇文章,思路来自这篇文章,文章链接:http://www.cnblogs.com/endlesscoding/p/6670432.html
不过根据我的需求,数据每次都在变化,所以都只能从后台绑定,先来看一下完成后的效果吧
可以看到,数据源是一年内一到十二月的金额,所以X轴是固定的,而Y轴标尺是根据数据源的最大值向上取100。再来计算每个标尺该显示的数值。
数据点的显示,也是根据提供数据的比例,来计算出像素值的
从头开始吧,先来说xaml,xaml中需要一个Canvas控件,之后所有的图形就是画在这里面
不会用Canvas的话可以先学习下官方文档:https://msdn.microsoft.com/zh-cn/library/system.windows.controls.canvas(v=vs.110).aspx
1 <Grid Height="400" Width="645"> 2 <Grid.ColumnDefinitions> 3 <ColumnDefinition Width="150" /> 4 <ColumnDefinition Width="330"/> 5 <ColumnDefinition Width="*"/> 6 </Grid.ColumnDefinitions> 7 <Grid.RowDefinitions> 8 <RowDefinition Height="25" /> 9 <RowDefinition />10 </Grid.RowDefinitions>11 <j:JLabel Label="企业账号:" Grid.Column="0" Grid.Row="0">12 <TextBlock Text="{Binding Userid}" HorizontalAlignment="Left" Foreground="Red"/>13 </j:JLabel>14 <j:JLabel Label="企业名称:" Grid.Column="1" Grid.Row="0">15 <TextBlock Text="{Binding Username}" HorizontalAlignment="Left" Foreground="Red"/>16 </j:JLabel>17 <j:JLabel Label="总金额(元):" Grid.Column="2" Grid.Row="0">18 <TextBlock Text="{Binding Pay_Total}" HorizontalAlignment="Left" Foreground="Red"/>19 </j:JLabel>20 <Canvas x:Name="chartCanvas" Margin="5" Grid.Column="0" Grid.Row="1" Grid.ColumnSpan="4">21 </Canvas>22 </Grid>
先来画横纵坐标和箭头吧,x1,y1,x2,y2这四个参数是Line在Canvas中的起点终点位置像素值
同样,Line类官方文档:https://msdn.microsoft.com/zh-cn/library/system.windows.shapes.line(v=vs.110).aspx
1 /// <summary> 2 /// 生成横纵坐标及箭头 3 /// </summary> 4 private void DrawArrow() 5 { 6 Line x_axis = new Line();//x轴 7 Line y_axis = new Line();//y轴 8 x_axis.Stroke = System.Windows.Media.Brushes.Black; 9 y_axis.Stroke = System.Windows.Media.Brushes.Black;10 x_axis.StrokeThickness = 3;11 y_axis.StrokeThickness = 3;12 x_axis.X1 = 40;13 x_axis.Y1 = 320;14 x_axis.X2 = 600;15 x_axis.Y2 = 320;16 y_axis.X1 = 40;17 y_axis.Y1 = 320;18 y_axis.X2 = 40;19 y_axis.Y2 = 30;20 this.chartCanvas.Children.Add(x_axis);21 this.chartCanvas.Children.Add(y_axis);22 23 Line y_scale1 = new Line(); //坐标原点直角24 y_scale1.Stroke = System.Windows.Media.Brushes.Black;25 y_scale1.StrokeThickness =1;26 y_scale1.X1 = 40;27 y_scale1.Y1 = 310;28 y_scale1.X2 = 44;29 y_scale1.Y2 = 310;30 y_scale1.StrokeStartLineCap = PenLineCap.Triangle;31 this.chartCanvas.Children.Add(y_scale1);32 33 Path x_axisArrow = new Path();//x轴箭头34 Path y_axisArrow = new Path();//y轴箭头35 x_axisArrow.Fill = new SolidColorBrush(Color.FromRgb(0, 0, 0));36 y_axisArrow.Fill = new SolidColorBrush(Color.FromRgb(0, 0, 0));37 PathFigure x_axisFigure = new PathFigure();38 x_axisFigure.IsClosed = true;39 x_axisFigure.StartPoint = new Point(600, 316); //路径的起点40 x_axisFigure.Segments.Add(new LineSegment(new Point(600, 324), false)); //第2个点41 x_axisFigure.Segments.Add(new LineSegment(new Point(610, 320), false)); //第3个点42 PathFigure y_axisFigure = new PathFigure();43 y_axisFigure.IsClosed = true;44 y_axisFigure.StartPoint = new Point(36, 30); //路径的起点45 y_axisFigure.Segments.Add(new LineSegment(new Point(44, 30), false)); //第2个点46 y_axisFigure.Segments.Add(new LineSegment(new Point(40, 20), false)); //第3个点47 PathGeometry x_axisGeometry = new PathGeometry();48 PathGeometry y_axisGeometry = new PathGeometry();49 x_axisGeometry.Figures.Add(x_axisFigure);50 y_axisGeometry.Figures.Add(y_axisFigure);51 x_axisArrow.Data = x_axisGeometry;52 y_axisArrow.Data = y_axisGeometry;53 this.chartCanvas.Children.Add(x_axisArrow);54 this.chartCanvas.Children.Add(y_axisArrow);55 56 TextBlock x_label =new TextBlock();57 TextBlock y_label =new TextBlock();58 TextBlock o_label =new TextBlock();59 x_label.Text = "月";60 y_label.Text = "元";61 o_label.Text = "0";62 Canvas.SetLeft(x_label, 610);63 Canvas.SetLeft(y_label, 20);64 Canvas.SetLeft(o_label, 20);65 Canvas.SetTop(x_label, 317);66 Canvas.SetTop(y_label, 4);67 Canvas.SetTop(o_label, 312);68 x_label.FontSize = 14;69 y_label.FontSize = 14;70 o_label.FontSize = 14; 71 this.chartCanvas.Children.Add(x_label);72 this.chartCanvas.Children.Add(y_label);73 this.chartCanvas.Children.Add(o_label);74 75 }
标尺,X轴以45为间隔单位,Y轴以10px为单位,且没5格显示一个大标尺
1 /// <summary> 2 /// 作出x轴和y轴的标尺 3 /// </summary> 4 private void DrawScale() 5 { 6 for (int i = 1; i < 13; i++)//作12个刻度 7 { 8 //原点 O=(40,320) 9 Line x_scale = new Line(); //主x轴标尺10 x_scale.StrokeEndLineCap = PenLineCap.Triangle;11 x_scale.StrokeThickness = 1;12 x_scale.Stroke = new SolidColorBrush(Color.FromRgb(0, 0, 0));13 x_scale.X1 = 40 + i * 45; 14 x_scale.X2 = x_scale.X1; 15 x_scale.Y1 = 320; 16 x_scale.StrokeThickness = 3;17 x_scale.Y2 = x_scale.Y1 - 8;18 this.chartCanvas.Children.Add(x_scale);19 20 Line x_in = new Line();//x轴轴辅助标尺21 x_in.Stroke = System.Windows.Media.Brushes.LightGray;22 x_in.StrokeThickness = 0.5;23 x_in.X1 = 40 + i * 45;24 x_in.Y1 = 320;25 x_in.X2 = 40 + i * 45;26 x_in.Y2 = 30;27 this.chartCanvas.Children.Add(x_in);28 }29 for (int j = 0; j < 30; j++ )30 {31 Line y_scale = new Line(); //主Y轴标尺32 y_scale.StrokeEndLineCap = PenLineCap.Triangle;33 y_scale.StrokeThickness = 1;34 y_scale.Stroke = new SolidColorBrush(Color.FromRgb(0, 0, 0));35 36 y_scale.X1 = 40; //原点x=4037 if (j % 5 == 0)38 {39 y_scale.StrokeThickness = 3;40 y_scale.X2 = y_scale.X1 + 8;//大刻度线41 }42 else43 {44 y_scale.X2 = y_scale.X1 + 4;//小刻度线45 }46 47 y_scale.Y1 = 320 - j * 10; //每10px作一个刻度 48 y_scale.Y2 = y_scale.Y1; 49 this.chartCanvas.Children.Add(y_scale);50 }51 for (int i = 1; i < 6; i++)52 {53 Line y_in = new Line();//y轴辅助标尺54 y_in.Stroke = System.Windows.Media.Brushes.LightGray;55 y_in.StrokeThickness = 0.5;56 y_in.X1 = 40;57 y_in.Y1 = 320 - i * 50;58 y_in.X2 = 600;59 y_in.Y2 = 320 - i * 50;60 this.chartCanvas.Children.Add(y_in);61 }62 63 }
刻度标签的话,X轴是固定的,并且其中用到了一个把阿拉伯数字转换为中文的方法 NumberToChinese(),
Y轴标尺标签,是用出入的 list<double>,计算出最大值再向上取100整,再分成五份,每份的值就是五个标签了
list最大值向上取100的方法:(除100向上取整再乘100)
Math.Ceiling(list.Max() / 100) * 100
1 /// <summary> 2 /// 添加刻度标签 3 /// </summary> 4 private void DrawScaleLabel(List<double> list) 5 { 6 for (int i = 1; i < 13; i++) 7 { 8 TextBlock x_ScaleLabel = new TextBlock(); 9 x_ScaleLabel.Text = NumberToChinese(i.ToString());10 if (x_ScaleLabel.Text == "一零")11 {12 x_ScaleLabel.Text = "十";13 Canvas.SetLeft(x_ScaleLabel, 40 + 45 * i - 6);14 }15 else if (x_ScaleLabel.Text == "一一")16 {17 x_ScaleLabel.Text = "十一";18 Canvas.SetLeft(x_ScaleLabel, 40 + 45 * i - 10);19 }20 21 else if (x_ScaleLabel.Text == "一二")22 {23 x_ScaleLabel.Text = "十二";24 Canvas.SetLeft(x_ScaleLabel, 40 + 45 * i - 10);25 }26 else27 {28 Canvas.SetLeft(x_ScaleLabel, 40 + 45 * i - 6);29 }30 Canvas.SetTop(x_ScaleLabel, 320 + 2);31 this.chartCanvas.Children.Add(x_ScaleLabel);32 }33 34 for (int i = 1; i < 6; i++)35 {36 TextBlock y_ScaleLabel = new TextBlock();37 double max = Math.Ceiling(list.Max() / 100) * 100;38 y_ScaleLabel.Text = (i * (max/5)).ToString();39 Canvas.SetLeft(y_ScaleLabel, 40 - 30); 40 Canvas.SetTop(y_ScaleLabel, 320 - 5 * 10 * i - 6); 41 42 this.chartCanvas.Children.Add(y_ScaleLabel);43 }44 }45 46 /// <summary>47 /// 数字转汉字48 /// </summary>49 /// <param name="numberStr"></param>50 /// <returns></returns>51 public static string NumberToChinese(string numberStr)52 {53 string numStr = "0123456789";54 string chineseStr = "零一二三四五六七八九";55 char[] c = numberStr.ToCharArray();56 for (int i = 0; i < c.Length; i++)57 {58 int index = numStr.IndexOf(c[i]);59 if (index != -1)60 c[i] = chineseStr.ToCharArray()[index];61 }62 numStr = null;63 chineseStr = null;64 return new string(c);65 }
接下来就是计算数据点了,难点在于计算像素点,X轴是固定的,所以不用关注
直接算好的X轴十二个数值
double[] left = { 85, 130, 175, 220, 265, 310, 355, 400, 445, 490, 535, 580 };
而Y轴就要自己算了,提供一个思路:
区域总像素 - 区域总像素 * (数值/最大值)
1 /// <summary> 2 /// 计算数据点并添加 3 /// </summary> 4 /// <param name="list"></param> 5 private void DrawPoint(List<double> list) 6 { 7 double[] left = { 85, 130, 175, 220, 265, 310, 355, 400, 445, 490, 535, 580 }; 8 List<double> leftlist = new List<double>(); 9 leftlist.AddRange(left);10 11 for (int i = 1; i < 13; i++)12 { 13 Ellipse Ellipse = new Ellipse();14 Ellipse .Fill = new SolidColorBrush(Color.FromRgb(0, 0, 0xff));15 Ellipse .Width = 8;16 Ellipse .Height = 8;17 Canvas.SetLeft(Ellipse,leftlist[i-1]- 4);18 double y_Max = Math.Ceiling(list.Max() / 100) * 100;19 Canvas.SetTop(Ellipse, 320 - 250 * (list[i-1] / y_Max) - 4); 20 coordinatePoints.Add(new Point(leftlist[i-1], 320 - 250 * (list[i-1] / y_Max)));21 this.chartCanvas.Children.Add(Ellipse);22 //值显示23 TextBlock EP_Label = new TextBlock();24 EP_Label.Foreground = System.Windows.Media.Brushes.Red;25 EP_Label.Text = list[i-1].ToString();26 Canvas.SetLeft(EP_Label, leftlist[i-1] - 10);27 Canvas.SetTop(EP_Label, 320 - 250 * (list[i-1] / y_Max) - 20);28 this.chartCanvas.Children.Add(EP_Label);29 }30 }
在绘制数据点的时候,每一次的位置都保存了: coordinatePoints.Add(new Point(leftlist[i-1], 320 - 250 * (list[i-1] / y_Max)));
先得定义:
/// <summary> /// 折线图坐标点 /// </summary> private PointCollection coordinatePoints = new PointCollection();
最后直接连连看就好了
1 /// <summary> 2 /// 绘制连接折线 3 /// </summary> 4 private void DrawCurve() 5 { 6 Polyline curvePolyline = new Polyline(); 7 8 curvePolyline.Stroke = Brushes.Green; 9 curvePolyline.StrokeThickness = 2;10 11 curvePolyline.Points = coordinatePoints;12 this.chartCanvas.Children.Add(curvePolyline);13 }
由于我项目的关系,数据是从DataGrid控件行数据来的,所以每一次都不一样,只能在弹出窗体时调用这几个方法
由于每一都不一样,在窗口关闭时需要清空画布内的所有控件,否则画布内控件会一直覆盖
chartCanvas.Children.Clear();coordinatePoints.Clear();
我的邮箱:alonezying@163.com 欢迎交流学习
- WPF在Canvas中绘图实现折线统计图
- WPF在Canvas中绘图实现折线统计图
- canvas绘图折线图表
- C# WPF Canvas绘图
- 使用QWT实现折线统计图
- canvas实现折线图
- Android中Canvas绘图方法的实现
- 统计图折线
- 使用ECharts实现各种数据统计图(饼图,柱状图,折线图)在javaWeb中的应用
- html5 canvas 实现折线图
- Html5中Canvas绘图
- WPF中绘图
- 在WPF中的Canvas上实现控件的拖动、缩放
- java实现各种数据统计图(柱形图,饼图,折线图)
- java实现各种数据统计图(柱形图,饼图,折线图)
- java实现各种数据统计图(柱形图,饼图,折线图)
- java实现各种数据统计图(柱形图,饼图,折线图)
- java实现各种数据统计图(柱形图,饼图,折线图)
- 艳辉音乐阁
- POJ 计算几何入门题目推荐
- 求有向图强联通分量--Tarjan算法
- 面试题41(2). 和为S的连续正数序列
- 《C++ Concurrency in Action》笔记5 std::thread::id
- WPF在Canvas中绘图实现折线统计图
- 201709 半集训
- codevs 1228(DFS序+线段树/树状数组)
- Java对Collections进行分组
- draw able和mipmap的区别
- python猜数字游戏
- 数据SQL脚本自动转换成javaBean代码的实现
- python字符处理
- java学习笔记-I/O篇(1)