C# 之 WPF 统计图表开发方案

一、前言

  • 本项目的统计图使用LiveCharts 控件集成。
  • LiveCharts, 官网:https://lvcharts.net 是一款简单,灵活,交互式和强大的 DOTNET数据可视化图表控件,内置多种统计图表,可满足本项目的需求。

二、环境配置

1、开发环境

  • 操作系统名称:Microsoft Windows 10 专业工作站版
  • IDE名称:Visual Studio 2017 15.9.9

2、加载 LiveCharts 库

这里演示通过命令行(DOTNET的包管理器NuGet)向中央仓库拉取 LiceCharts 库。

  • 在控制台键入下面的命令
PM> Install-Package LiveCharts.Wpf
  • 或者转到解决方案资源管理器,右键单击引用,然后管理NuGet包
    NuGet
  • 浏览LiveCharts.Wpf,选择包并单击安装
    安装LiveCharts

3、添加必须的头文件

  • 将命名空间添加到XAML
xmlns:lvc="clr-namespace:LiveCharts.Wpf;assembly=LiveCharts.Wpf"
  • 在逻辑代码中添加头文件
using LiveCharts;
using LiveCharts.Wpf;

三、基础图形

LiveCharts旨在为用户提供便利,一切都自动更新和动画,库只会在认为有必要时更新,每次数据更改,添加/删除系列或添加/删除图表时都会更新将自己更新,除了你的业务,你真的不需要担心任何事情,让LiveCharts处理图表。

有许多类型已准备好已定义的绘图,您可以在 LiveCharts 官网(https://lvcharts.net)了解更多信息,下面介绍几种常用的图表视图绘制。

1、柱状图(ColumnCharts)

  • 将命名空间添加到XAML
xmlns:lvc="clr-namespace:LiveCharts.Wpf;assembly=LiveCharts.Wpf"
  • 在逻辑代码(MainWindows.xaml.cs)中添加头文件
using LiveCharts;
using LiveCharts.Wpf;
  • 编写界面代码
<Grid>
    <!--SeriesCollection 为柱状数据-->
    <lvc:CartesianChart Series="{Binding SeriesCollection}" LegendLocation="Left">
        <lvc:CartesianChart.AxisX>
            <!--Labels 为图形标签数据-->
            <lvc:Axis Title="Salesman" Labels="{Binding Labels}"/>
        </lvc:CartesianChart.AxisX>
        <lvc:CartesianChart.AxisY>
            <!--Formatter 为Y轴单位-->
            <lvc:Axis Title="Sold Apps" LabelFormatter="{Binding Formatter}"/>
        </lvc:CartesianChart.AxisY>
    </lvc:CartesianChart>
</Grid>

  • 逻辑代码(为方便查漏补缺把所有的代码都贴出来)
using LiveCharts;
using LiveCharts.Wpf;
using System;
using System.Windows;

namespace LiveChartsTest
{
    /// <summary>
    /// MainWindow.xaml 的交互逻辑
    /// </summary>
    public partial class MainWindow : Window
    {
    	// 构造函数
        public MainWindow()
        {
            InitializeComponent();
            SeriesCollection = new SeriesCollection
            {
                new ColumnSeries
                {
                    Title = "2015",
                    // 2015的条形值
                    // 动态改变数据只需要更改此处的 Values 便可
                    Values = new ChartValues<double> { 10, 50, 39, 50 }
                }
            };

            //adding series will update and animate the chart automatically
            SeriesCollection.Add(new ColumnSeries
            {
                Title = "2016",
                // 2016的条形值
                // 动态改变数据只需要更改此处的 Values 便可
                Values = new ChartValues<double> { 11, 56, 42 }
            });

            //also adding values updates and animates the chart automatically
            SeriesCollection[1].Values.Add(48d);

            Labels = new[] { "Maria", "Susan", "Charles", "Frida" };
            Formatter = value => value.ToString("N");

            DataContext = this;
        }

        // 图形数据属性
        public SeriesCollection SeriesCollection { get; set; }
        // 标签属性
        public string[] Labels { get; set; }
        // Y轴坐标属性
        public Func<double, string> Formatter { get; set; }
    }
}
  • 运行效果如下:
    效果

2、饼状图(PieCharts)

  • 因为头文件都是一样的,这里就不贴了,此处有疑问请查看柱状图(LineCharts)

  • 直接上XAML代码:

<lvc:PieChart LegendLocation="Bottom" 
	DataClick="Chart_OnDataClick" Hoverable="False" DataTooltip="{x:Null}">
    <lvc:PieChart.Series>
        <!--DataLabels属性是显示模块标签的、PointLabel是占比-->
        <lvc:PieSeries Title="Maria" Values="3" DataLabels="True"
                       LabelPoint="{Binding PointLabel}"/>
        <lvc:PieSeries Title="Charles" Values="4" DataLabels="True" 
                       LabelPoint="{Binding PointLabel}"/>
        <lvc:PieSeries Title="Frida" Values="6" DataLabels="True" 
                       LabelPoint="{Binding PointLabel}"/>
        <lvc:PieSeries Title="Frederic" Values="2" DataLabels="True" 
                       LabelPoint="{Binding PointLabel}"/>
    </lvc:PieChart.Series>
</lvc:PieChart>
  • 业务逻辑代码
// 构造函数
 public Pie()
 {
     InitializeComponent();
     // 自定义显示标签
     PointLabel = chartPoint =>
         string.Format("{0} ({1:P})", chartPoint.Y, chartPoint.Participation);

     DataContext = this;
 }

 // 使用泛型动态传入数据
 public Func<ChartPoint, string> PointLabel { get; set; }

 /**
  *  饼图鼠标点击事件
  */ 
 private void Chart_OnDataClick(object sender, ChartPoint chartpoint)
 {
     var chart = (LiveCharts.Wpf.PieChart)chartpoint.ChartView;

     //clear selected slice.
     foreach (PieSeries series in chart.Series)
         series.PushOut = 0;

     var selectedSeries = (PieSeries)chartpoint.SeriesView;
     selectedSeries.PushOut = 8;
 }
  • 运行效果:
    效果图

3、折线图(LineCharts)

  • XAML文件
<!--LegendLocation 表示图例的位置在右边-->
<lvc:CartesianChart Series="{Binding SeriesCollection}" LegendLocation="Right" >
    <lvc:CartesianChart.AxisY>
        <!-- YFormatter Y轴坐标值-->
        <lvc:Axis Title="Sales" LabelFormatter="{Binding YFormatter}"></lvc:Axis>
    </lvc:CartesianChart.AxisY>
    <lvc:CartesianChart.AxisX>
        <!-- Labels X轴坐标值-->
        <lvc:Axis Title="Month" Labels="{Binding Labels}"></lvc:Axis>
    </lvc:CartesianChart.AxisX>
</lvc:CartesianChart>
  • 业务逻辑代码
// 构造函数
public Line()
{
    InitializeComponent();

    SeriesCollection = new SeriesCollection
    {
        // 匿名新建LineSeries对象
        new LineSeries
        {
            // 折线的名称
            Title = "Series 1",
            // 折线的拐点值
            Values = new ChartValues<double> { 4, 6, 5, 2 ,4 }
        },
        new LineSeries
        {
            Title = "Series 2",
            Values = new ChartValues<double> { 6, 7, 3, 4 ,6 },
            // 折线拐点的表示形状,默认为原点
            PointGeometry = null
        },
        new LineSeries
        {
            Title = "Series 3",
            Values = new ChartValues<double> { 4,2,7,2,7 },
            // 折线拐点的表示形状,默认为原点,这里是正方形
            PointGeometry = DefaultGeometries.Square,
            // 折线拐点的形状尺寸
            PointGeometrySize = 15
        }
    };

    // X轴的坐标值
    Labels = new[] { "Jan", "Feb", "Mar", "Apr", "May" };
    // Y轴坐标值
    YFormatter = value => value.ToString("C");

    //modifying the series collection will animate and update the chart
    // 动态添加折线"Series 4"
    SeriesCollection.Add(new LineSeries
    {
        Title = "Series 4",
        Values = new ChartValues<double> { 5, 3, 2, 4 },
        LineSmoothness = 0, //0: straight lines, 1: really smooth lines
        PointGeometry = Geometry.Parse("m 25 70.36218 20 -28 -20 22 -8 -6 z"),
        PointGeometrySize = 50,
        // 折线拐点的表示形状,默认为原点,这里是正方形
        PointForeground = Brushes.Gray
    });

    //modifying any series values will also animate and update the chart
    // 动态为折线"Series 4"添加一个值
    SeriesCollection[3].Values.Add(5d);

    // 数据绑定源 
    DataContext = this;
}

// 相关属性
public SeriesCollection SeriesCollection { get; set; }
public string[] Labels { get; set; }
public Func<double, string> YFormatter { get; set; }
  • 效果图:
    效果图

4、散点图(SactterCharts)

  • XAML文件
<!--LegendLocation 表示图例位置-->
<lvc:CartesianChart Grid.Row="1" LegendLocation="Bottom">
    <lvc:CartesianChart.Series>
        <!--Values 表示 A\B\C 的散点图值-->
        <lvc:ScatterSeries Title="Series A" Values="{Binding ValuesA}" />
        <!--PointGeometry表示散点的形状-->
        <lvc:ScatterSeries Title="Series B" Values="{Binding ValuesB}"
                           PointGeometry="{x:Static lvc:DefaultGeometries.Diamond}" />
        <!--StrokeThickness 表示散点形状大小-->
        <!--Fill 表示填充内容-->
        <lvc:ScatterSeries Title="Series C" Values="{Binding ValuesC}"
                           PointGeometry="{x:Static lvc:DefaultGeometries.Triangle}"
                           StrokeThickness="2" Fill="Transparent"/>
    </lvc:CartesianChart.Series>
    <lvc:CartesianChart.AxisY>
        <!--setting the axis unit improved the labels rounding rule-->
        <!--Unit 表示坐标的密度单元-->
        <lvc:Axis Unit="1"></lvc:Axis>
    </lvc:CartesianChart.AxisY>
</lvc:CartesianChart>
  • 业务逻辑代码:
// 构造函数
public Scatter()
{
    InitializeComponent();

    // 使用随机数填充三种类型的散点
    var r = new Random();
    ValuesA = new ChartValues<ObservablePoint>();
    ValuesB = new ChartValues<ObservablePoint>();
    ValuesC = new ChartValues<ObservablePoint>();

    // 赋随机值
    for (var i = 0; i < 20; i++)
    {
        ValuesA.Add(new ObservablePoint(
        	r.NextDouble() * 10, r.NextDouble() * 10));
        ValuesB.Add(new ObservablePoint(
        	r.NextDouble() * 10, r.NextDouble() * 10));
        ValuesC.Add(new ObservablePoint(
        	r.NextDouble() * 10, r.NextDouble() * 10));
    }

    // 数据源绑定
    DataContext = this;
}

// 相关属性
public ChartValues<ObservablePoint> ValuesA { get; set; }
public ChartValues<ObservablePoint> ValuesB { get; set; }
public ChartValues<ObservablePoint> ValuesC { get; set; }
  • 效果图:
    效果图

5、堆叠图(StackedCharts)

  • 对于堆叠图,相比之前的统计图而言,头文件有所不同
  • 添加到XAML中
xmlns:lvc="clr-namespace:LiveCharts.Wpf;assembly=LiveCharts.Wpf"
  • 添加到业务逻辑代码中
using LiveCharts;
using LiveCharts.Defaults;
using LiveCharts.Wpf;
  • XAML文件:
<!--通过SeriesCollection加载堆叠图,LegendLocation表图例-->
<lvc:CartesianChart 
	Grid.Row="2" Series="{Binding SeriesCollection}"  LegendLocation="Right">
    <lvc:CartesianChart.AxisX>
        <!--X轴坐标值-->
        <lvc:Axis Title="Year" LabelFormatter="{Binding XFormatter}"/>
    </lvc:CartesianChart.AxisX>
    <lvc:CartesianChart.AxisY>
        <!--Y轴坐标值-->
        <lvc:Axis Title="Population" LabelFormatter="{Binding YFormatter}"/>
    </lvc:CartesianChart.AxisY>
</lvc:CartesianChart>
  • 业务逻辑代码:
public Stacked()
{
    InitializeComponent();
    SeriesCollection = new SeriesCollection
    {
        new StackedAreaSeries
        {
            Title = "Africa",
            // Values 描点
            Values = new ChartValues<DateTimePoint>
            {
                new DateTimePoint(new DateTime(1950, 1, 1), .228),
                new DateTimePoint(new DateTime(1960, 1, 1), .145),
                new DateTimePoint(new DateTime(1970, 1, 1), .366),
                new DateTimePoint(new DateTime(1980, 1, 1), .34),
                new DateTimePoint(new DateTime(1990, 1, 1), .629),
                new DateTimePoint(new DateTime(2000, 1, 1), .242),
                new DateTimePoint(new DateTime(2010, 1, 1), 1.031),
                new DateTimePoint(new DateTime(2013, 1, 1), 1.110)
            },
            LineSmoothness = 0
        },
        new StackedAreaSeries
        {
            Title = "N & S America",
            // Values 描点
            Values = new ChartValues<DateTimePoint>
            {
                new DateTimePoint(new DateTime(1950, 1, 1), .456),
                new DateTimePoint(new DateTime(1960, 1, 1), .424),
                new DateTimePoint(new DateTime(1970, 1, 1), .31),
                new DateTimePoint(new DateTime(1980, 1, 1), 1.618),
                new DateTimePoint(new DateTime(1990, 1, 1), .247),
                new DateTimePoint(new DateTime(2000, 1, 1), .81),
                new DateTimePoint(new DateTime(2010, 1, 1), .942),
                new DateTimePoint(new DateTime(2013, 1, 1), .432)
            },
            LineSmoothness = 0
        },
        new StackedAreaSeries
        {
            Title = "Asia",
            // Values 描点
            Values = new ChartValues<DateTimePoint>
            {
                new DateTimePoint(new DateTime(1950, 1, 1), 0.395),
                new DateTimePoint(new DateTime(1960, 1, 1), 1.694),
                new DateTimePoint(new DateTime(1970, 1, 1), 0.128),
                new DateTimePoint(new DateTime(1980, 1, 1), 1.634),
                new DateTimePoint(new DateTime(1990, 1, 1), 0.213),
                new DateTimePoint(new DateTime(2000, 1, 1), 1.717),
                new DateTimePoint(new DateTime(2010, 1, 1), 0.165),
                new DateTimePoint(new DateTime(2013, 1, 1), 0.298)
            },
            LineSmoothness = 0
        },
        new StackedAreaSeries
        {
            Title = "Europe",
            // Values 描点
            Values = new ChartValues<DateTimePoint>
            {
                new DateTimePoint(new DateTime(1950, 1, 1),  .228),
                new DateTimePoint(new DateTime(1960, 1, 1),  .145),
                new DateTimePoint(new DateTime(1970, 1, 1),  .366),
                new DateTimePoint(new DateTime(1980, 1, 1),  .34),
                new DateTimePoint(new DateTime(1990, 1, 1),  .629),
                new DateTimePoint(new DateTime(2000, 1, 1), .231),
                new DateTimePoint(new DateTime(2010, 1, 1), .740),
                new DateTimePoint(new DateTime(2013, 1, 1), .742)
            },
            LineSmoothness = 0
        }
    };

    // X、Y坐标
    XFormatter = val => new DateTime((long)val).ToString("yyyy");
    YFormatter = val => val.ToString("N") + " M";

    // 数据绑定
    DataContext = this;
}

// 相关属性
public SeriesCollection SeriesCollection { get; set; }
public Func<double, string> XFormatter { get; set; }
public Func<double, string> YFormatter { get; set; }
  • 效果图:
    效果图

6、其他(OtherCharts)

  • LiveCharts 还有很多很好看的图表,但是多数的用法都是和以上几种常见的图表的使用方式都是一样的,剩下的就不重复累赘了,如果想要尝试可以参考上述五种图表。

四、总结

1、LiveCharts的优势

  • LiveCharts 使用简单,快速集成,更加容易的集成在项目上。

2、LiveCharts的略势

  • LiveCharts 集成简单,但是数据更新是导致整个页面都是重新绘制的,更新数据重新刷新界面的方式对用户不太友好,LiveCharts 也有相应的自动刷新的控件,可以免费体验15天,但是到期需要付费使用。
  • 实现图标动态刷新的方式还是相对简单粗暴,LiveCharts 内部刷新机制是当 Series、Values等等这个数值发生变化时,后台会自动触发图标界面刷新功能,从而重回整个 View。
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值