Skip to main content Skip to footer

How to Aggregate WPF Chart Data Using FlexChart

Charts can be used to visualize and analyze data. Sometimes, we need to show summarized data in the chart for which the aggregation is used. In ComponentOne FlexChart for WPF, UI is separated from data manipulation to improve the product's performance, unlike the legacy C1Chart. Hence, FlexChart does not have built-in functionality for data manipulation.

In this topic, we will demonstrate the steps necessary to achieve aggregation of the WPF chart control data. A complete sample can be downloaded at the end.

Ready to Try FlexChart? Download ComponentOne Today!

WPF Chart Aggregation

Here is our WPF chart control unaggregated.

Unaggregated WPF Chart

A common user requirement is to summarize the data at runtime by quarter. The Sum aggregate type adds all the values together, and the chart displays a single bar for each quarter.

Quarter Sum WPF Chart

The nice thing about making this a run-time feature is that the end user can choose how to view the data. For example, they may want to summarize the data by year instead.

Year Sum WPF Chart

Typically aggregation is done by simply summing all values together. But there are more aggregate types that users can analyze, including Average, Count, Min, and Max. The example below uses the “Max” aggregate type, which simply displays the maximum value found for each aggregate group.

Year Max WPF Chart

To achieve the aggregation functionality like Sum, Avg, Max, Min, and Count in FlexChart for WPF, let’s go through the next section with code examples.

Creating the Model and Enum

To best create this runtime feature, we will create model and enumeration classes. This will enable it to be reusable in more forms.

Create the Model Class for the FlexChart Data. This would be unique to your dataset.

public class AggregateItem 
{
  public int Year { get; set; }
  public int Q { get; set; }
  public string M { get; set; }
  public double Value { get; set; }
}

Create Enum for the Aggregate Type.

public enum AggregateType
{
   Sum,
   Avg,
   Max,
   Min,
   Count
}

 

Creating Aggregate Extension

Next, we will create an Extension method for Aggregate to perform aggregation basis of AggregateType on data. Extension methods enable you to "add" methods to existing types without creating a new derived type. Here, we have created an Aggregate method in the Extension.

public static class AggregateExtensions
{
   public static double Aggregate(this IEnumerable<double> values, AggregateType type, Func<IEnumerable<double>, double> customFun = null)
   {
      if (customFun != null)
      {
          return customFun(values);
      }
      switch (type)
      {
      case AggregateType.Avg:
         return values.Average();
      case AggregateType.Sum:
         return values.Sum();
      case AggregateType.Max:
         return values.Max();
      case AggregateType.Min:
         return values.Min();
      case AggregateType.Count:
         return values.Count();
      default:
         return values.Sum();
      }
   }
}

Creating ViewModel for the FlexChart

The ViewModel acts as the data source for our View, and we can control the View through ViewModel. We created some properties and methods in ViewModel which can be bound to the view.

For the data collection, we expose a DataSource. Typically, your Views and ViewModels will implement INotifyPropertyChanged so that you can pass updates to any property to the View.

  1. Update Bindings

Update Bindings whenever Aggregate Property updates.

        public string AggregateProperty
        {
            get
            {
                return _aggregateProperty;
            }
            set
            {
                string key = value;
                if (string.IsNullOrEmpty(key))
                {
                    Bindings = null;
                }

                switch (key)
                {
                    case "Year":
                        Bindings = new string[] { "Year" };
                        break;
                    case "Quarter":
                        Bindings = new string[] { "Year", "Q" };
                        break;
                    default:
                        Bindings = null;
                        break;
                }
                _aggregateProperty = key;
                UpdateAggregation();
            }
        }
  1. Update Aggregation

Update Aggregate DataSource based on Binding, AggregateProperty, and AggregateType. The updated DataSource (bounded to FlexChart’s ItemsSource) will reflect the FlexChart view.

       public void UpdateAggregation()
        {
            IEnumerable<object> src = this.Source as IEnumerable<object>;

            if (src == null)
            {
                return;
            }
            if (!src.Any())
            {
                return;
            }

            IEnumerable<KeyValuePair<string, double>> data;

            // Updating DataSource
            if (Bindings == null || Bindings.Length == 0)
            {
                data = from p in src
                       select new KeyValuePair<string, double>(GetValueKey(p, new string[] { "Year", "M" }), GetValue(p, "Value"));
               
            }
            else
            {
                var groupedData = src.GroupBy(k => GetValueKey(k, Bindings));
                data = from p in groupedData select new KeyValuePair<string, double>(GetValueKey(p.First(), Bindings).ToString(), (from k in p select GetValue(k, "Value")).Aggregate(AggregateType));
               
            }

            this.DataSource = data.ToArray();
            this.OnPropertyChanged("DataSource");
        }
        string GetValueKey(object obj, string[] keys)
        {
            string r = string.Empty;
            foreach (var key in keys)
            {
                switch (key)
                {
                    case "Q":
                        r += " Q" + GetValue(obj, key);
                        break;
                    case "M":
                        DateTime dt = new DateTime(1900, Convert.ToInt32(GetValue(obj, key)), 1);
                        r += " " + dt.ToString("MMM");
                        break;
                    default:
                        r += GetValue(obj, key);
                        break;
                }
            }
            return r;
        }

        protected static double GetValue(object obj, string binding)
        {
            var v = Convert.ToDouble(obj.GetType().GetProperty(binding).GetValue(obj, null));

            return v;
        }

Creating FlexChart and Binding to ViewModel in XAML

At last, we have to create a FlexChart in XAML so it can be visible in View. But the most important part is to bind the View with ViewModel. It can be done by setting the View's DataContext property to an instance of your ViewModel.

<Window
        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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:FlexChartAggregate"
        xmlns:c1="http://schemas.componentone.com/winfx/2006/xaml" x:Class="FlexChartAggregate.MainWindow"
        xmlns:ViewModel="clr-namespace:FlexChartAggregate.ViewModel"
        mc:Ignorable="d" WindowState="Maximized"
        Title="MainWindow" Height="450" Width="800">
    <Window.DataContext>
        <ViewModel:AggregateViewModel/>
    </Window.DataContext>
    <Grid x:Name="grid">
        <Grid.RowDefinitions>
                <RowDefinition Height="Auto" />
                <RowDefinition Height="*" />
            </Grid.RowDefinitions>
            <StackPanel MinHeight="48" Grid.Row="0" Orientation="Horizontal">
            <ComboBox x:Name="cbProperty" Margin="5" MinWidth="120" Height="24" ItemsSource="{Binding AggregateProperties}" DisplayMemberPath="Key" SelectedValuePath="Value"  SelectedValue="{Binding AggregateProperty}"/>
                <ComboBox x:Name="cbTopNCount" Margin="5" MinWidth="120" Height="24" ItemsSource="{Binding AggregateTypes}"  DisplayMemberPath="Key" SelectedValuePath="Value"  SelectedValue="{Binding AggregateType}" SelectedIndex="0"/>
                <ComboBox x:Name="cbChartType" Margin="5" MinWidth="120" Height="24" ItemsSource="{Binding ChartTypes}"  DisplayMemberPath="Key" SelectedValuePath="Value"  SelectedValue="{Binding ChartType}" SelectedIndex="0"/>
            </StackPanel>
        <c1:C1FlexChart x:Name="flexChart1" Grid.Row="1"
                            Header="Sales data" BindingX="Key" LegendToggle="True"
                            ItemsSource="{Binding DataSource}" ChartType="{Binding ChartType}" >
            <c1:C1FlexChart.AxisX>
                <c1:Axis LabelAngle="45"/>
            </c1:C1FlexChart.AxisX>
            <c1:Series SeriesName="Value" Binding="Value"/>
            </c1:C1FlexChart>
    </Grid>
</Window>

 

This is helpful for anyone migrating from C1Chart or any FlexChart user.

Download the sample for complete implementation.

Ready to Try FlexChart? Download ComponentOne Today!

comments powered by Disqus