Skip to main content Skip to footer

Using C1Chart in MVVM Part II

In one of my earlier blog posts I dealt with using C1Chart in MVVM. I was asked if we could dynamically add and remove data series from C1Chart. My answer to that is, yes there is way and I decided to post this as another blog post since this would be a common scenario for many. If we inspect the various bindings available to us in the C1Chart model we will find that the ChartData accepts a binding. This is the answer to the above question. We can simply have a C1ChartData property exposed from the ViewModel and at run-time add and remove series from the ChartData. This will be more clear as we walk through the implementation. Let's start by creating a new WPF project inside Visual Studio. I am going to show data based on "Sales" class. So let's first create this class. Here is the code:


public  class Sales:INotifyPropertyChanged  
{  
        public Sales(string product,double salevalue,double volume,string shipcity,double discount)  
        {  
            _product = product;  
            _salevalue = salevalue;  
            _volume = volume;  
            _shipcity = shipcity;  
            _discount = discount;  
            \_variance = (\_volume*1000/_salevalue);  
        }  
          #region privateFields  
        string _product;  
        double _salevalue;  
        double _volume;  
        double _discount;  
        string _shipcity;  
        double _variance;  
            #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 Discount  
         {  
             get { return _discount; }  
             set { _discount = value; OnPropertyChanged("Discount"); }  
         }  
         public double Variance  
         {  
             get { return (this.Volume*1000/ this.SaleValue); }  
         }  
         public string ShipCity  
         {  
             get { return _shipcity; }  
             set { _shipcity = value; OnPropertyChanged("ShipCity"); }  
         }  
          #endregion  

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

In this class we have six fields: Products, Salevalue (sales value of a product), Volume (volume of product sold), Discount (discount given on that product), ShipCity (cities where the products where sold), and Variance (a hypothetical variance). Next we will create a base ViewModel which will implement the INotifyPropertyChanged interface. Our ChartViewModel will inherit the base viewmodel. Here is the code for the base viewmodel.


      public class ViewModelBase:INotifyPropertyChanged  
      {  
          #region INotifyPropertyChanged Members  
            public event PropertyChangedEventHandler PropertyChanged;  
          protected void OnPropertyChanged(string property)  
          {  
              if (PropertyChanged != null)  
              {  
                  PropertyChanged(this, new PropertyChangedEventArgs(property));  
              }  
          }  
          #endregion  
          public ViewModelBase()  
          {  
          }  
     }  

Now lets create the ChartViewModel. Here is the code.


     public class ChartViewModel:ViewModelBase  
     {  
         #region Ctor..  
         public ChartViewModel()  
         {  
             _saleslist = new ObservableCollection<Sales>();  
             LoadData();  
             _axisvalues = new ObservableCollection<string>();  
             _axisvalues.Add("SaleValue");  
             _axisvalues.Add("Volume");  
             _axisvalues.Add("VolumeVsCity");  
             _axisvalues.Add("ProductDiscount");  
             _axisvalues.Add("VolumeTotal");  
             \_axisview = new CollectionView(\_axisvalues);  
             \_axisview.CurrentChanged += new EventHandler(\_axisview_CurrentChanged);  

            _chartdata = new ChartData();  
            ChartDataView.ItemNameBinding = new Binding("Product");  

            DataSeries ds1 = new DataSeries();  
            ds1.Label = "SaleValue";  
            ds1.ValueBinding = new Binding("SaleValue");  

            DataSeries ds2 = new DataSeries();  
            ds2.Label = "Volume";  
            ds2.ValueBinding = new Binding("Volume");  

            ChartDataView.ItemsSource = SalesList;  
            ChartDataView.Children.Add(ds1);  
            ChartDataView.Children.Add(ds2);  
         }  
         #endregion  

         #region private Fields  

         ObservableCollection<Sales> _saleslist;  
         ObservableCollection<string> _axisvalues;  
         CollectionView _axisview;  
                ChartData _chartdata;  
         #endregion  
         #region publicProperties  
         public ObservableCollection<Sales> SalesList  
         {  
             get { return _saleslist; }  
             set { _saleslist = value; }  
         }  
         public CollectionView AxisView  
         {  
             get { return _axisview; }  
             set { _axisview = value; OnPropertyChanged("AxisView"); }  
         }  
        public ChartData ChartDataView  
         {  
             get { return _chartdata; }  
             set { _chartdata = value; OnPropertyChanged("ChartDataView"); }  
         }  
                 #endregion  
         #region privateMethods  
         void LoadData()  
         {  
             _saleslist.Add(new Sales("Confectionaries", 2500.00,300000.00,"NewYork",25.50));  
             _saleslist.Add(new Sales("Plastics", 3500.00,720000.00,"Newark",15.75));  
             _saleslist.Add(new Sales("Electronics", 7500.00,800000.00,"GeorgeTown",20.65));  
             _saleslist.Add(new Sales("Produces", 800.00,100000.00,"Houston",30.35));  
         }  
         void \_axisview\_CurrentChanged(object sender, EventArgs e)  
         {  
             if (_axisview.CurrentItem.ToString() == "SaleValue")  
            {  
                 _chartdata.Children.Clear();  

                 DataSeries ds1 = new DataSeries();  
                 ds1.Label = "SaleValue";  
                 ds1.ValueBinding = new Binding("SaleValue");  
                 ds1.ItemsSource = SalesList;  
                   _chartdata.Children.Add(ds1);  
                 _chartdata.ItemNameBinding = new Binding("Product");  
                              }  
             else if (_axisview.CurrentItem.ToString() == "Volume")  
             {  
                 _chartdata.Children.Clear();  
                 DataSeries ds2 = new DataSeries();  
                 ds2.ValueBinding = new Binding("Volume");  
                 ds2.Label = "Volume";  
                 ds2.ItemsSource = SalesList;  
                 _chartdata.Children.Add(ds2);  
                 _chartdata.ItemNameBinding = new Binding("Product");  
             }  
             else if (_axisview.CurrentItem.ToString() == "VolumeVsCity")  
             {  
                 _chartdata.Children.Clear();  
                 DataSeries ds1 = new DataSeries();  
                 ds1.Label="Volume";  
                 ds1.ValueBinding = new Binding("Volume");  
                 ds1.ItemsSource = SalesList;  
                 _chartdata.Children.Add(ds1);  
                 _chartdata.ItemNameBinding = new Binding("ShipCity");  
             }  
             else if (_axisview.CurrentItem.ToString() == "ProductDiscount")  
             {  
                 _chartdata.Children.Clear();  
                 DataSeries ds1 = new DataSeries();  
                 ds1.Label = "Discount";  
                 ds1.ValueBinding = new Binding("Discount");  
                 ds1.ItemsSource = SalesList;  
                 _chartdata.Children.Add(ds1);  
                _chartdata.ItemNameBinding = new Binding("Product");  
               }  
             else if (_axisview.CurrentItem.ToString() == "VolumeTotal")  
             {  
                 _chartdata.Children.Clear();  
                 DataSeries ds1 = new DataSeries();  
                 ds1.Label = "Volume";  
                 ds1.ValueBinding = new Binding("Volume");  
                 ds1.ItemsSource = SalesList;  
                  DataSeries ds2 = new DataSeries();  
                  ds2.Label = "Variance";  
                 ds2.ValueBinding = new Binding("Variance");  
                 ds2.ItemsSource = SalesList;  
                 _chartdata.Children.Add(ds1);  
                 _chartdata.Children.Add(ds2);  
                 _chartdata.ItemNameBinding = new Binding("Product");  
               }  
         }  
         #endregion  
     }  

Ok, lets inspect the above code. There is a LoadData() method which loads data to ObservableCollection SalesList. There is an ObservableCollection _axisvalues collection which basically is a collection exposing the dataseries that we are going to make available at runtime. We have added various values to this collection in the constructor. Next is the ChartDataView property which is of type ChartData. In the constructor we have created two series and added them to the ChartData. Now for the scenario when the series are added and removed from the ChartData. We do that in the _axisview_CurrentChanged. The _axisview is a collectionview of _axisvalues. In this event we check the current selection and add a dataseries to chartdata. Now we just need to bind the Data property of C1Chart to ChartDataView property of the viewmodel. So here is a basic UI XAML for our application.


 <Window x:Class="C1ChartinMVVMPart2.MainWindow"  
          xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"  
          xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"  
          Title="MainWindow" Height="336" Width="790" xmlns:c1chart="http://schemas.componentone.com/xaml/c1chart">  
        <Grid  x:Name="LayoutRoot"  >  
          <c1chart:C1Chart Height="276" HorizontalAlignment="Left" Margin="153,9,0,0" Name="c1Chart1" VerticalAlignment="Top" Width="603"  Data="{Binding ChartDataView}" Theme="{DynamicResource {ComponentResourceKey ResourceId=Vista, TypeInTargetAssembly=c1chart:C1Chart}}">  
                          <c1chart:C1ChartLegend DockPanel.Dock="Right" />  
          </c1chart:C1Chart>  
          <ListBox Height="100" ItemsSource="{Binding AxisView}" HorizontalAlignment="Left" Margin="27,98,0,0" Name="listBox1" VerticalAlignment="Top" Width="120"  Background="{Binding ElementName=c1Chart1,Path=Background}"/>  
                </Grid>  
  </Window>  

As we can see I have exposed the AxisView collectionview to the ListBox. The ListBox would show all the available data series and upon selection the relevant data series would be drawn inside the chart. Add the following code inside the Application startup event:


MainWindow win = new MainWindow();  
C1ChartinMVVMPart2.ViewModel.ChartViewModel vm = new ViewModel.ChartViewModel();  
win.LayoutRoot.DataContext = vm;  
win.Show();  

Run the application and observe the behavior. Download Sample Thanks for reading!

MESCIUS inc.

comments powered by Disqus