Skip to main content Skip to footer

C1Chart Dynamic Axis Binding in MVVM

When working with C1Chart in MVVM-style applications, it is sometimes possible that the chart in the view may not know the property it is binding to until runtime. For example, the C1Chart DataSeries' ValueBinding might be set during runtime. This is a trivial task unless we are using some design pattern such as MVVM. In this article I will walk you through creating dynamic axis binding in C1Chart for WPF according to MVVM. Let’s start by creating a new WPF application. Add the following Sales class to the project, the details are self explanatory.


public  class Sales:INotifyPropertyChanged  
    {  
      public Sales(string product,double salevalue,double volume)  
      {  
          _product = product;  
          _salevalue = salevalue;  
          _volume = volume;  
          \_itemvariance = (\_volume /_salevalue)*100;  
      }  
        #region privateFields  
      string _product;  
      double _salevalue;  
      double _volume;  
      double _itemvariance;  
        #endregion  
        #region publicProperties  
      public string Product  
      {  
          get { return _product; }  
          set { _product = value; }  
      }  
      public double SaleValue  
      {  
          get { return _salevalue; }  
          set { _salevalue = value; OnPropertyChanged("SaleValue"); }  
      }  
      public double Volume  
      {  
          get { return _volume; }  
          set { _volume = value; OnPropertyChanged("Volume"); }  
      }  
      public double ItemVariance  
       {  
           get {return _itemvariance; }  

       }  
        #endregion  
        #region INotifyPropertyChanged Members  

       public event PropertyChangedEventHandler PropertyChanged;  
       public void OnPropertyChanged(string property)  
       {  
           if (PropertyChanged != null)  
           {  
               PropertyChanged(this, new PropertyChangedEventArgs(property));  
           }  
       }  
       #endregion  
    }  

Next we will create ViewModelBase class which our ChartViewModel will inherit:


public class ViewModelBase:INotifyPropertyChanged  
{  
#region INotifyPropertyChanged Members  

public event PropertyChangedEventHandler PropertyChanged;  

#endregion  
public ViewModelBase()  
{  
}  
protected void OnPropertyChanged(string property)  
{  
if (PropertyChanged != null)  
{  
PropertyChanged(this, new PropertyChangedEventArgs(property));  
}  
}  
}  

Now let’s create our ChartViewModel class.


public class ChartViewModel:ViewModelBase  
{  
#region Ctor..  
public ChartViewModel()  
{  
_saleslist = new ObservableCollection<Sales>();  
LoadData();  
_axisvalues = new ObservableCollection<string>();  
PropertyInfo[] pi = typeof(Sales).GetProperties();  
//add the properties of Sales class as axis value collection  

foreach (PropertyInfo p in pi)  
{  
if (p.Name == "Product")  
{  
}  
else  
{  
_axisvalues.Add(p.Name);  
}  
}  
\_axisview = new CollectionView(\_axisvalues);  

}  
#endregion  
#region private Fields  
ObservableCollection<Sales> _saleslist;  
ObservableCollection<string> _axisvalues;  
CollectionView _axisview;  

#endregion  
#region publicProperties  
public ObservableCollection<Sales> SalesList  
{  
get { return _saleslist; }  
set { _saleslist = value; }  
}  

public CollectionView AxisView  
{  
get { return _axisview; }  
set { _axisview = value; OnPropertyChanged("AxisView"); }  
}  
#endregion  
#region privateMethods  
void LoadData()  
{  
_saleslist.Add(new Sales("Confectionaries", 2500.00,3000.00));  
_saleslist.Add(new Sales("Plastics", 3500.00,7200.00));  
_saleslist.Add(new Sales("Electronics", 7500.00,8000.00));  
_saleslist.Add(new Sales("Produces", 800.00,1000.00));  
_saleslist.Add(new Sales("Machinary", 18000.00, 11000.00));  
}  

#endregion  
}  

In this class we simply create a SalesList collection and load data to it. The AxisView collection would give us the different chart series that we need to show, in this scenario they are “SaleValue”,”Volume” and “ItemVariance.” Next we create the ModelDataSeries class inheriting from C1Chart dataseries which resolves the actual property binding at run time. This class creates ValueNameProperty dependency property which we will bind to the chart series.


public class ModelDataSeries : DataSeries  
{  
public string ValueName  
{  
get { return (string)GetValue(ValueNameProperty); }  
set { SetValue(ValueNameProperty, value); }  
}  

public static readonly DependencyProperty ValueNameProperty =  
DependencyProperty.Register("ValueName", typeof(string), typeof(ModelDataSeries), new UIPropertyMetadata(OnValueNameChanged));  

static void OnValueNameChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)  
{  
var ds = (ModelDataSeries)obj;  
if (string.IsNullOrEmpty(ds.ValueName))  
ds.ValueBinding = null;  
else  
ds.ValueBinding = new Binding(ds.ValueName);  
ds.Invalidate();  
}  
}  

Next drop a C1Chart and listbox on the MainWindow. Add the name space for the ViewModel as below: xmlns:local="clr-namespace:DynamicAxisBinding" Set the ItemSource of the ListBox to AxisView. Here is the XAML:


ItemsSource="{Binding AxisView}"  

Set the datacontext of the chart such as:


DataContext="{Binding}"  

Set ChartData ItemNameBinding to the Poduct field of the Sales class as below:


ItemNameBinding="{Binding Path=Product}"  

Now we need to add series to the chart. We will add the ModelDataSeries as follows: <local:ModelDataSeries ValueName="{Binding AxisView.CurrentItem}" Label="{Binding AxisView.CurrentItem}"/> That’s all we need to do. The complete XAML for MainWindow is as below:


<Window x:Class="DynamicAxisBinding.MainWindow"  
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"  
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"  
     xmlns:local="clr-namespace:DynamicAxisBinding"  
     Title="Chart View" Height="350" Width="782" xmlns:c1chart="http://schemas.componentone.com/xaml/c1chart">  
     <Grid>  
          <ListBox Height="100" HorizontalAlignment="Left" Margin="0,73,0,0" Name="listBox1" VerticalAlignment="Top" Width="120" ItemsSource="{Binding AxisView}" />  
          <c1chart:C1Chart Height="264" HorizontalAlignment="Left" Margin="135,27,0,0" Name="c1Chart1" VerticalAlignment="Top" Width="613" Theme="{DynamicResource {ComponentResourceKey ResourceId=Office2007Blue, TypeInTargetAssembly=c1chart:C1Chart}}">  
         <c1chart:C1Chart.Data>  
                 <c1chart:ChartData DataContext="{Binding}" ItemsSource="{Binding SalesList}" ItemNameBinding="{Binding Product}" >  
                     <local:ModelDataSeries  ValueName="{Binding AxisView.CurrentItem}" Label="{Binding AxisView.CurrentItem}"/>  
                 </c1chart:ChartData>  
         </c1chart:C1Chart.Data>  
        <c1chart:C1ChartLegend DockPanel.Dock="Right" />  
      </c1chart:C1Chart>  
   </Grid>  
</Window>  

Finally we set the DataContext of “MainWindow”. I would do that in the Application_Startup event under App.cs. Here is code for that:


private void Application_Startup(object sender, StartupEventArgs e)  
{  
   DynamicAxisBinding.ViewModel.ChartViewModel vm = new ViewModel.ChartViewModel();  
   MainWindow _win = new MainWindow();  
   _win.DataContext = vm;  
   _win.Show();  
}  

Run the sample and observe that as you select different views from the listbox the chart is drawn accordingly. Download Sample

MESCIUS inc.

comments powered by Disqus