2

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?".

Joe
  • 1,304
  • 1
  • 10
  • 22

1 Answers1

4

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.

BionicCode
  • 1
  • 4
  • 28
  • 44
  • Thanks BionicCode.That enables the binding just fine. However, my Fruit Class is created by Entity Framework and populated with integer data values from the database. So I need to convert the integer values of the fruit properties derived from the EF class into the ChartValues properties that I use to define the pie chart segments. It seems that I will have to convert the double type of the properties in the EF derived class to the ChartValues type of the PieSeries elements. Apples = (ChartValues)Apples_EF; Fails. Can you suggest a solution? – Joe Sep 02 '19 at 02:05
  • You need to create a wrapper class for the chart as data model. I updated the answer to address your problem. – BionicCode Sep 02 '19 at 07:31
  • Bionic you wouldn't believe how helpful you are. I have pasted all your recommendations into the code above. If you can just please stay with me and advise how to solve the System.NullReferenceException: 'Object reference not set to an instance of an object.' exception. – Joe Sep 02 '19 at 10:18
  • Sure. Have you called `CreatePieChartData(Fruit fruitData)`? Where is the exception thrown? Please help. – BionicCode Sep 02 '19 at 10:37
  • Yes. I've pasted the latest version of the code over the original. You'll see I call CreatePieChartData(Fruit fruitData) from inside the PieChartExample() constructor. It seems to hit the exception when it tries to execute the XAML Values="{Binding FruitPieChartModel.AppleDataSeries}" – Joe Sep 02 '19 at 12:13
  • Ah, I see. Thank you. This makes sense as the `InitializeComponent` is invoked before the property `FruitPieChartModel` is initialized. Therefore the bindings won\t refresh afterwards as we are binding to a common CLR property. I replaced this property with a `DependencyProperty`. You can copy it from the updated example. I wanted to keep it simple that's why didn't implement the property as a `DependencyProperty` in first the place. Better aways use a `DependencyProperty` inside a control when you need a property. – BionicCode Sep 02 '19 at 13:18
  • Sorry BionicCode, I can't see where you created the Dependency Property. Would you mind pasting it into the example code for me? – Joe Sep 02 '19 at 13:45
  • Sorry, my bad. Seems like I forgot to save the edit. But NOW it's there. I think everything should work now. – BionicCode Sep 02 '19 at 14:16
  • 1
    Bingo! Thanks for your vital and essential input. – Joe Sep 02 '19 at 14:43