Search code examples
c#wpfdata-bindinglivecharts

Bind LiveCharts Pie Chart PieSeries Values to properties in the code behind


I have the following a class containing properties that get their data values from a database.

    public partial class Fruit
    {

        public double Apples { get; set; }
        public double Oranges { get; set; }
        public double Grapes { get; set; }
        public double Bananas { get; set; }
    }

I want to bind these properties to the PieSeries Values in a LiveCharts PieChart.

When I try Values="{Binding Source= Apples}" the control breaks. Can anyone help? My code is below. C#

using LiveCharts;
using LiveCharts.Wpf;
using System;
using System.Windows;
using System.Windows.Controls;

namespace Wpf.PieChart
{
    public partial class PieChartExample : UserControl
    {
        public Fruit Fruit => new Fruit();
        public Fruit fruitData = new Fruit();
        public static readonly DependencyProperty FruitPieChartModelProperty = DependencyProperty.Register(
                                                "FruitPieChartModel",
                                                typeof(FruitPieChartDataModel),
                                                typeof(PieChartExample),
                                                new PropertyMetadata(default(FruitPieChartDataModel)));

        public FruitPieChartDataModel FruitPieChartModel
        {
            get => (FruitPieChartDataModel)GetValue(PieChartExample.FruitPieChartModelProperty);
            set => SetValue(PieChartExample.FruitPieChartModelProperty, value);
        }

        public PieChartExample()
        {
            InitializeComponent();
            PointLabel = chartPoint =>
                string.Format("{0} ({1:P})", chartPoint.Y, chartPoint.Participation);
            DataContext = this;
            CreatePieChartData(fruitData);
        }
        private void CreatePieChartData(Fruit fruitData)
        {
            this.FruitPieChartModel = new FruitPieChartDataModel(fruitData);
        }
        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;
        }
    }
    public class FruitPieChartDataModel
    {
        public FruitPieChartDataModel(Fruit entityFrameworkDataModel)
        {
            this.AppleDataSeries = new ChartValues<double>() { entityFrameworkDataModel.Apples };
            this.OrangeDataSeries = new ChartValues<double>() { entityFrameworkDataModel.Oranges };
            this.GrapeDataSeries = new ChartValues<double>() { entityFrameworkDataModel.Grapes };
            this.BananaDataSeries = new ChartValues<double>() { entityFrameworkDataModel.Bananas };
        }

        public ChartValues<double> AppleDataSeries { get; set; }
        public ChartValues<double> OrangeDataSeries { get; set; }
        public ChartValues<double> GrapeDataSeries { get; set; }
        public ChartValues<double> BananaDataSeries { get; set; }
    }

    public partial class Fruit
    {
        public double Apples { get; set; } = 6;
        public double Oranges { get; set; } = 4;
        public double Grapes { get; set; } = 8;
        public double Bananas { get; set; } = 5;
    }
}

XAML

<UserControl
    x:Class="Wpf.PieChart.PieChartExample"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:local="clr-namespace:Wpf.PieChart"
    xmlns:lvc="clr-namespace:LiveCharts.Wpf;assembly=LiveCharts.Wpf"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    d:DataContext="{d:DesignInstance local:PieChartExample}"
    d:DesignHeight="300"
    d:DesignWidth="500"
    mc:Ignorable="d">
    <Grid>
        <lvc:PieChart
            DataClick="Chart_OnDataClick"
            DataTooltip="{x:Null}"
            Hoverable="False"
            LegendLocation="Bottom">
            <lvc:PieChart.Series>
                <lvc:PieSeries
                    Title="Apples"
                    DataLabels="True"
                    LabelPoint="{Binding PointLabel}"
                    Values="{Binding FruitPieChartModel.AppleDataSeries}" />
                <lvc:PieSeries
                    Title="Oranges"
                    DataLabels="True"
                    LabelPoint="{Binding PointLabel}"
                    Values="{Binding FruitPieChartModel.OrangeDataSeries}" />
                <lvc:PieSeries
                    Title="Grapes"
                    DataLabels="True"
                    LabelPoint="{Binding PointLabel}"
                    Values="{Binding FruitPieChartModel.GrapeDataSeries}" />
                <lvc:PieSeries
                    Title="Bananas"
                    DataLabels="True"
                    LabelPoint="{Binding PointLabel}"
                    Values="{Binding FruitPieChartModel.BananaDataSeries}" />
            </lvc:PieChart.Series>
        </lvc:PieChart>
    </Grid>
</UserControl>

This code incorporates the valuable suggestions made by BionicCode.

Unfortunately I cant get the software to run because I am encountering a "System.NullReferenceException: 'Object reference not set to an instance of an object.'

Please advise what I am doing wrong?".


Solution

  • The property Apples doesn't exist and instance of Fruitexists. You have to add a property to your UserControl and create an instance of Fruit. Additionally the type of the properties must be a collection of type ChartValues<T>.

    Since Fruit is a POCO returned by the Entity Framework you have to create a wrapper class for charts to serve as the chart model.

    FruitPieChartDataModel.cs

    public class FruitPieChartDataModel
    {
      public FruitPieChartDataModel(Fruit entityFrameworkDataModel)
      {
        this.AppleDataSeries = new ChartValues<double>() { entityFrameworkDataModel.Apples };
        this.OrangeDataSeries = new ChartValues<double>() { entityFrameworkDataModel.Oranges };
        this.GrapeDataSeries = new ChartValues<double>() { entityFrameworkDataModel.Grapes };
        this.BananaDataSeries = new ChartValues<double>() { entityFrameworkDataModel.Bananas };
      }
    
      public ChartValues<double> AppleDataSeries { get; set; }
      public ChartValues<double> OrangeDataSeries { get; set; }
      public ChartValues<double> GrapeDataSeries { get; set; }
      public ChartValues<double> BananaDataSeries { get; set; }
    }
    

    PieChartExample.xaml.cs

    public partial class PieChartExample : UserControl
    {   
      public static readonly DependencyProperty FruitPieChartModelProperty = DependencyProperty.Register(
        "FruitPieChartModel",
        typeof(FruitPieChartDataModel),
        typeof(PieChartExample),
        new PropertyMetadata(default(FruitPieChartDataModel)));
    
      public FruitPieChartDataModel FruitPieChartModel
      {
        get => (FruitPieChartDataModel) GetValue(PieChartExample.FruitPieChartModelProperty);
        set => SetValue(PieChartExample.FruitPieChartModelProperty, value);
      }
    
      private void CreatePieChartData(Fruit fruitData)
      {
        this.FruitPieChartModel = new FruitPieChartDataModel(fruitData);
      }
    
      ...
    }
    

    Then you can bind the pie chart to the FruitPieChartModel property:

    PieChartExample.xaml

    <lvc:PieSeries Title="Apples" 
                   Values="{Binding FruitPieChartModel.AppleDataSeries}" 
                   DataLabels="True"
                   LabelPoint="{Binding PointLabel}"/>
    

    If you want to set a Binding on the FruitPieChartModel property (as a binding target) then you must make it a DependencyProperty.