Creating Cool WPF Charts using the ModernUI Chart Library and WebAPI

Posted by: Mahesh Sabnis , on 7/14/2014, in Category WPF
Views: 68647
Abstract: WPF has a rich charting and graphical capability, however it is time-consuming to build your own charts from groundup. The ModernUI chart library is a free handy library which allows you to quickly display some statistical data in a graphical form. This article shows how to use this Library in your applications.

A picture is worth a thousand words! When working with business applications, a major challenge is to present data in a graphical form that is not only accurate, but also easy to interpret. Charts are a popular medium of presenting data and results graphically and it becomes the responsibility of the team to make use of the most suitable technology and chart type, which can be data bound to a variety of data sources.

This article is published from the DotNetCurry .NET Magazine – A Free High Quality Digital Magazine for .NET professionals published once every two months. Subscribe to this eMagazine for Free and get access to hundreds of free tutorials from experts

Windows Presentation Foundation (WPF), is a widely used popular .NET technology which can be used to create Rich UX experiences for the end-user and provides a variety of graphical representations based on the WPF graphical capabilities. One of its useful graphical representation is the 2-D Shape API containing Line, Polyline, Rectangle, Ellipse etc., using which graphical charts can be created. However creating a feature rich graphical representation can be a very tedious task. Implementing different chart-types like the Pie, Bar, Column etc. can be very time consuming. It would help if a library exists that can be used to add simple graphs and charts in your application. What would be even nicer is if the same library can be used to create WPF as well as Windows Store apps.

The ModernUI Charts is one such free library created by Torsten Mandelkow which can be used in your WPF applications and also conceptually supports Windows Store Apps as well as Silverlight applications.

Disclaimer: The ModernUI Charts can be freely used in your application and are published under Microsoft Public License which grants you these rights. However these charts are in Beta and has not been updated since May 2013. So it is advisable that before using it any production ready application, make sure you have tested in thoroughly. Having said that, our purpose of showcasing this free library is to give you a head start if you are planning to build your own chart controls. You can even build on top of the ModernUI Chart library and create something that matches your business requirements. If you want a charting control that is high performing and production ready, try Lightning Chart from Arction.

The chart-types provided out-of-the-box in the ModernUI Charts library are as listed here:

- PieChart

- BarChart

  • ClusteredBarChart
  • StackedBarChart
  • StackedBarChart100Percent

- ColumnChart

  • ClusteredColumnChart
  • StackedColumnChart
  • StackedColumnChart100Percent

- Doughnut Chart

- Radial Gauge Chart

That is a quite a nice list for something that is available free of cost!

Creating an Application using the ModernUI Chart library

Installing the ModernUI Chart

Follow these simple steps to install the ModernUI chart library in your application

Step 1: Download the solution project for the Modern UI Chart from the following link http://modernuicharts.codeplex.com/

Step 2: Open the solution in Visual Studio 2012/2013 and build it. This should give you the ModernUI Chart libraries for WPF, Silverlight and Windows 8 Application development. For our demo, we will only be using the ModernUI Chart library for WPF and for that, we will be referencing it in a WPF 4.5 project which we will create shortly.

Note: The ModernUI Chart is still in its Beta, so for all custom requirements you need to extend the library on your own. The source code for it is available and for those who are really interested, that’s all that is needed to get started!

Obtaining a Data Source - AdventureWorks2012 Database

For our application, we will be using an Adventure Works database to give you a feel of a real application. The Database can be downloaded from the following link: http://msftdbprodsamples.codeplex.com/releases/view/55330

Once you have downloaded the AdventureWorks2012_Data.mdf file, integrate it with a SQL Server Instance on your server. We are using SQL Server 2012. We will be specifically using the SalesTerritory table from the database for displaying Sales data using ModernUI Charts.

Creating an Application Using WEB API and WPF

In our application, we will be using ASP.NET WEB API to fetch our sales data and make it available to the WPF Application for displaying charts. Follow these steps:

Step 1: Open Visual Studio 2013 and create a new blank solution, name it as ‘Modern_Charts_Service’. In this solution, add a new WEB API project. Call it as ‘WebApi_Service’.

Step 2: To this project, add a new ADO.NET Entity Data model and select AdventureWorks2012 database. From this database select the SalesTerritory table for mapping. After completing the wizard, the designer will look like the following

sales-data

Figure 1: Sales Territory Table

In the application, we will using this table (as seen in Figure 1) to display chart by CountryRegionCode. For e.g. US, GB, etc.

Step 3: In the project, add a new WEB API controller project using Entity Framework:

webapi-client

Figure 2: WebAPI 2 Controller project

Name the controller as ‘SalesTerritoriesController’. This step will generate the code for data access from the database, using Entity Framework.

Now build the project.

Step 4: To the solution created in the first 3 steps, add a new WPF 4.5 application and call it ‘WPF_ModernChart_Client’. In this project, add the assembly reference for the ModernUI Chart library for WPF. At the beginning of the article, we had discussed how to obtain this library. Once the library is added to your project, the project reference will look like the following in Solution Explorer:

modern-ui-chart-ref

Figure 3: ModernUI Chart library

Step 5: In the project (bin\Debug folder) add an xml file with the name ‘ChartNames.xml’. This file will store the different chart names in it:


                      

We will be using this file to read the available chart types and display them on UI. The respective chart will be automatically drawn based upon the selected chart type.

Step 6: Add a new folder with the name ‘HelperClasses’ to the project. In this folder, add a new class file and declare some classes in it.

using System;
using System.Collections.ObjectModel;
using System.Linq;
using System.Xml.Linq;

namespace WPF_ModernChart_Client.HelperClasses
{
    ///      /// The class used to store the Chart Name and its number     /// from the xml file.      /// 
    public class ChartNameStore
    {
        public int Number { get; set; }
        public string Name { get; set; }
    }

    ///      /// The class used to load Chart types form the XML file.     /// 
    public class ChartTypeHelper
    {
        ObservableCollection _ChartsNames;          ///          /// The Method Load the Xml file and return chart names.         ///          ///          public ObservableCollection GetChartNames()         {             _ChartsNames = new ObservableCollection();               XDocument xDoc = XDocument.Load("ChartNames.xml");              var Charts = from c in xDoc.Descendants("ChartTypes").Elements("Chart")                          select c;              foreach (var item in Charts)             {                 _ChartsNames.Add(new ChartNameStore()                 {                   Name =  item.Attribute("Name").Value,                  Number =Convert.ToInt32(item.Attribute("Number").Value)                 });             }              return _ChartsNames;          }     } } 

The ChartNameStore class is used for representing the chart information and the ChartTypeHelper class contains the GetChartNames method which loads and reads the xml file created in the previous step and returns all the Chart names.

Step 7: Since we are using WEB API, we need to add the ASP.NET WEB API client libraries from NuGet, into our WPF project. This step will add all the necessary libraries to make a call to WEB API.

web-api-client-library

Figure 5: WPF Project references

Step 8: To the project, add a new folder with the name ‘ServiceAdapter’. In this folder add a new class file and write the following code:

using WPF_ModernChart_Client.ModelClasses;

namespace WPF_ModernChart_Client.ServiceAdapter
{
    ///      /// The class used to make call to WEB API and get the sales information     /// 
    public class ProxyAdapter
    {
        ObservableCollection _SalesData;          public async Task

The above code makes use of the HttpClient class which helps to make an asynchronous call to WEB API. The BaseAddress property of the HttpClient class accepts the URI of the WEB API. The call is made over the uri and when completed, the data is read from it.

Step 9: To the project, add a new folder with the name ‘ModelClasses’ and add the following classes in it:

namespace WPF_ModernChart_Client.ModelClasses
{
    ///      /// The entity class for Sales information     /// 
    public partial class SalesTerritory
    {
        public int TerritoryID { get; set; }
        public string Name { get; set; }
        public string CountryRegionCode { get; set; }
        public string Group { get; set; }
        public decimal SalesYTD { get; set; }
        public decimal SalesLastYear { get; set; }
        public decimal CostYTD { get; set; }
        public decimal CostLastYear { get; set; }
        public System.Guid rowguid { get; set; }
        public System.DateTime ModifiedDate { get; set; }
    }

    ///      /// The class which is used to provide the necessary information to draw chart     /// on the chart axis.     /// 
    public partial class SalesInfo
    {
        public string Name { get; set; }
        public decimal Sales { get; set; }
    }

    ///      /// The class used to represent information for the CountryRegioCode     /// e.g. US, GB etc.     /// 
    public class CountryRegionCode
    {
        public string CountryRegion { get; set; }
    }
}

In this code we just saw:

  • The SalesTerritory class represents the entity for the sales information received from the WEB API.
  • The SalesInfo class is used to provide the necessary information for drawing chart on the UI.
  • The CountryRegionCode class is used to represent the CountryRegion information on the UI, so that on the selection of it by end-user, a chart can be drawn.

Step 10: In the project add a new folder with the name ‘Commands’ and add the following command class in it.

using System;
using System.Windows.Input;

namespace WPF_ModernChart_Client.Commands
{
    public class RelayCommand : ICommand
    {
        Action _handler;

        public RelayCommand(Action h)
        {
            _handler =  h;
        }

        public bool CanExecute(object parameter)
        {
            return true;
        }

        public event EventHandler CanExecuteChanged;

        public void Execute(object parameter)
        {
            _handler();
        }
    }
}

Step 11: To draw a Chart, logic is required for reading and manipulating the received data from WEB API and then using it in a proper format to draw chart on the UI. Since we are using the SalesTerritory table to read the data, the data for columns for SalesYTD and SalesLastYear will be used for displaying Charts and the column CountryRegionCode will be used based upon which the data will be grouped to display on chart.

Add a new folder with the name ‘ViewModelsRepository’ in the project and add a class file to it with the following code:

using System.Collections.ObjectModel;
using System.Linq;
using WPF_ModernChart_Client.HelperClasses;

using WPF_ModernChart_Client.ModelClasses;
using WPF_ModernChart_Client.ServiceAdapter;
using System.ComponentModel;
using WPF_ModernChart_Client.Commands;

namespace WPF_ModernChart_Client.ViewModelsRepository
{
    ///      /// The class contains:     /// 1. The ChartInfo collection property to display the available charts in the combobox.     /// 2. The SalesData collection property to use as a source for drawing charts in UI.     /// 3. The CountryRegion collection property to use as a source for the CountryRegion code for UI.     /// 4. The CountryRegionName property to use as a filter when the UI changes the CountryRegion to draw chart.     /// 5. The IsRadioButtonEnabled property for enabling and disabling the RadioButton on UI     /// 6. The SalesDetailsYTDCommand and SalesDetailsLastYearCommand command object to execute methods when action is taken on UI     /// 7. The GetYTDSalesDataByCountryRegion() and GetLastYearSalesDataByCountryRegion() method are     /// used to get the YTD and LastYEar sales data respectively based upon the CountryRegionCode selected on UI.     /// 8. The GetCountryRegionCodeGroup() method is used to provide information  about available CountryRegion code.     /// 
    public class ChartsViewModel : INotifyPropertyChanged
    {
        ProxyAdapter adapter;

        ObservableCollection _ChartsInfo;          public ObservableCollection ChartsInfo         {             get { return _ChartsInfo; }             set { _ChartsInfo = value; }         }          ObservableCollection _SalesData;          public ObservableCollection SalesData         {             get { return _SalesData; }             set { _SalesData = value; }         }              ObservableCollection _CountryRegion;          public ObservableCollection CountryRegion         {             get { return _CountryRegion; }             set { _CountryRegion = value; }         }           CountryRegionCode _CountryRegionName;          public CountryRegionCode CountryRegionName         {             get { return _CountryRegionName; }             set             {                 _CountryRegionName = value;                 IsRadioButtonEnabled = true;                 onPropertyChanged("CountryRegionName");             }         }           bool _IsRadioButtonEnabled = false;          public bool IsRadioButtonEnabled         {             get { return _IsRadioButtonEnabled; }             set              {                 _IsRadioButtonEnabled = value;                 onPropertyChanged("IsRadioButtonEnabled");             }         }                    ChartTypeHelper helper;          public ChartsViewModel()         {             adapter = new ProxyAdapter();              helper = new ChartTypeHelper();              ChartsInfo = helper.GetChartNames();              SalesData = new ObservableCollection();             CountryRegion = new ObservableCollection();                           GetCountryRegionCodeGroup();               SalesDetailsYTDCommand = new RelayCommand(GetYTDSalesDataByCountryRegion);             SalesDetailsLastYearCommand = new RelayCommand(GetLastYearSalesDataByCountryRegion);         }          public RelayCommand SalesDetailsYTDCommand { get; set; }         public RelayCommand SalesDetailsLastYearCommand { get; set; }          ///          /// Method to get Sales Data for YTD based upon the CountryRegion code         ///          public async void GetYTDSalesDataByCountryRegion()         {             if (CountryRegionName!=null)             {              SalesData.Clear();             var Res = (from sale in await adapter.GetSalesInformation()                        where sale.CountryRegionCode == CountryRegionName.CountryRegion                        select new {                            Name = sale.Name,                           SaleYTD = sale.SalesYTD                        }).ToList();               foreach (var item in Res)              {                  SalesData.Add(new SalesInfo() { Name = item.Name, Sales = item.SaleYTD });              }             }         }           ///          /// Method to get Sales Data for Last Year based upon the CountryRegion code         ///          public async void GetLastYearSalesDataByCountryRegion()         {             if (CountryRegionName != null)             {              SalesData.Clear();             var Res = (from sale in await adapter.GetSalesInformation()                        where sale.CountryRegionCode == CountryRegionName.CountryRegion                        select new                        {                            Name = sale.Name,                            SalesLastYear = sale.SalesLastYear                        }).ToList();              foreach (var item in Res)             {                 SalesData.Add(new SalesInfo() { Name = item.Name, Sales = item.SalesLastYear });             }             }         }          ///          /// Method to Get the Country-Region Code         ///          public async void GetCountryRegionCodeGroup()         {              var Res = (from cr in await adapter.GetSalesInformation()                       group cr by cr.CountryRegionCode into crg                       select new CountryRegionCode()                       {                           CountryRegion = crg.Key                       });             foreach (var item in Res.ToList())             {                 CountryRegion.Add(item);             }         }          public event PropertyChangedEventHandler PropertyChanged;          void onPropertyChanged(string pName)         {             if (PropertyChanged != null)             {                 PropertyChanged(this,new PropertyChangedEventArgs(pName));             }         }     } }   

The code we just saw has the following specifications:

- The ChartInfo collection property is used to display the available charts in the combobox.

- The SalesData collection property is used as a source for drawing charts in UI.

- The CountryRegion collection property is used as a source for the CountryRegion code for UI.

- The CountryRegionName property is used as a filter when the UI changes the CountryRegion to draw chart.

- The IsRadioButtonEnabled property for enabling and disabling the RadioButton on the UI

- The SalesDetailsYTDCommand and SalesDetailsLastYearCommand command object to execute methods when action is taken on UI

- The GetYTDSalesDataByCountryRegion() and GetLastYearSalesDataByCountryRegion () method are used to get the YTD and LastYear sales data respectively based upon the CountryRegionCode selected on UI.

- The GetCountryRegionCodeGroup () method is used to provide information about the available CountryRegion code.

Step 12: In the Project, add a new folder with the name ‘ChartControls’. This folder will also contain some WPF UserControls. Each UserControl will contain the different chart types from the ModernUI Chart library. Add a new WPF User control in the project and name it as ‘ClusteredBarChartUserControl’. Define the User Control as following:

                                                                        


In the above XAML, the ModernUI Chart library is highlighted. The XAML defines properties set for the ‘ClusteredBarChart’. The ChartSeries defines the data source for drawing chart using the ‘ItemsSource’ property. The DisplayMemberName property displays the Textual data on the chart axis and ValueMember property is used to display values on chart axis.

Follow Step 12 to create user controls for the following chart types:

  • PieChart
  • ClusteredColumnChart
  • ClusteredBarChart
  • DoughnutChart
  • StackedColumnChart
  • StackedBarChart
  • RadialGaugeChart

Step 13: Open MainWindow.xaml and add the following XAML markup in it:

                                                                                                                                                                                                                                                                                                                                       

This XAML will produce the following output:

 

main-window-design

Figure 6: Sales Chart UI

The ComboBox ‘lstcharttype’ displays all the available chart types. The ComboBox ‘lstCountryRegionCode’ is used to display all the country regions in it so that end-user can select it to draw a chart. Both ComboBoxes are initially set to the default selected index to ‘0’. The Grid with the name ‘grdChartContainer’ (not seen in the UI) will be used as a container to the UserControl for displaying chart types as defined in Step 12. The Grid is bound with the SalesData property declared in the ViewModel class, using DataContext property of the Grid. This property will then be passed to each user control for drawing chart. Both radio buttons are set with the Command property declared in the ViewModel class.

Step 14: Add the following code in the code-behind of the MainWindow.xaml:

public partial class MainWindow : Window
    {

        ChartsViewModel vm;

         
        ///          /// The Constructor set the DataContext of the Window to an object of the ViewModel class.         /// 
        public MainWindow()
        {
            InitializeComponent();
            vm = new ChartsViewModel();
            this.DataContext = vm;
        }

 
        ///          /// The method gets executed when the chart is selected from the ComboBox.         /// The method adds the UserControl in the Grid with the name grdChartContainer based upon the chart name selected from the ComboBox.         /// 
        /// 
        /// 
        private void lstcharttype_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            grdChartContainer.Children.Clear();


            var chartType = lstcharttype.SelectedItem as ChartNameStore;

            switch (chartType.Name)
            {
                case "PieChart":
                    PieChartUserControl pie = new PieChartUserControl();
                    pie.DataContext = grdChartContainer.DataContext;
                    grdChartContainer.Children.Add(pie);
                    break;
                case "ClusteredColumnChart":
                    ClusteredColumnChartUserControl ccchart = new ClusteredColumnChartUserControl();
                    ccchart.DataContext = grdChartContainer.DataContext;
                    grdChartContainer.Children.Add(ccchart);
                    break;
                case "ClusteredBarChart":
                    ClusteredBarChartUserControl cbchart = new ClusteredBarChartUserControl();
                    cbchart.DataContext = grdChartContainer.DataContext;
                    grdChartContainer.Children.Add(cbchart);
                    break;
                case "DoughnutChart":
                    DoughnutChartUserControl dnchart = new DoughnutChartUserControl();
                    dnchart.DataContext = grdChartContainer.DataContext;
                    grdChartContainer.Children.Add(dnchart);
                    break;
                case "StackedColumnChart":
                    StackedColumnChartUserControl stcchart = new StackedColumnChartUserControl();
                    stcchart.DataContext = grdChartContainer.DataContext;
                    grdChartContainer.Children.Add(stcchart);
                    break;
                case "StackedBarChart":
                    StackedBarChartUserControl stbchart = new StackedBarChartUserControl();
                    stbchart.DataContext = grdChartContainer.DataContext;
                    grdChartContainer.Children.Add(stbchart);
                    break;
                case "RadialGaugeChart":
                    RadialGaugeChartUserControl rgchart = new RadialGaugeChartUserControl();
                    rgchart.DataContext = grdChartContainer.DataContext;
                    grdChartContainer.Children.Add(rgchart);
                    break;
            }

        }
    }

The code we just saw defines an object of the ViewModel class. This object is assigned to the DataContext property of the MainWindow so that all public properties defined in the ViewModel class can be bound with the UI in XAML. The lstcharttype_SelectionChanged method is executed when the chart is selected from the lstcharttype combobox. This methods then adds the Chart UserControl in the grid grdChartContainer.

Step 15: Run the application and you should see the following:

sales-chart-territory

Figure 7: Sales Chart for US

The default values for the Country Region is selected as ‘US’ (United States).

Pie Chart

The default Chart type selected is ‘PieChart’. Select the Radio Button ‘Sales YTD’ or ‘Sales Last Year’ and the result will change as seen in Figure 8:

pie-chart

Figure 8: Sales YTD or Sales Last Year

Now change the chart type and see how the graphical representation changes:

Radial Gauge Chart

radial-chart

Figure 9: Radial Gauge Chart

Similarly all the other chart types can be tested by selecting the respective Chart type from the ‘Select Chart Type’ dropdown.

Clustered Column Chart

clustered-column-chart

Figure 10: Clustered Column Chart

Clustered Bar Chart

clustered-bar-chart

Figure 11: Clustered Bar Chart

Doughnut Chart

doughnut-chart

Figure 12: Doughnut Chart

Stacked Column Chart

stacked-column-chart

Figure 13: Stacked Column Chart

Stacked Bar Chart

stacked-bar-chart

Figure 14: Stacked Bar Chart

Conclusion: WPF has a rich charting and graphical capability, however it is time-consuming to build your own charts from groundup. The ModernUI chart library is a free handy library which allows you to quickly display some statistical data in a graphical form instead of creating your own chart using WPF 2-D APIs or relying on a 3rd party graphic package.

Download the entire source code from our GitHub Repository at bit.ly/dncm13-modernuicharts

This article has been editorially reviewed by Suprotim Agarwal.

Absolutely Awesome Book on C# and .NET

C# and .NET have been around for a very long time, but their constant growth means there’s always more to learn.

We at DotNetCurry are very excited to announce The Absolutely Awesome Book on C# and .NET. This is a 500 pages concise technical eBook available in PDF, ePub (iPad), and Mobi (Kindle).

Organized around concepts, this Book aims to provide a concise, yet solid foundation in C# and .NET, covering C# 6.0, C# 7.0 and .NET Core, with chapters on the latest .NET Core 3.0, .NET Standard and C# 8.0 (final release) too. Use these concepts to deepen your existing knowledge of C# and .NET, to have a solid grasp of the latest in C# and .NET OR to crack your next .NET Interview.

Click here to Explore the Table of Contents or Download Sample Chapters!

What Others Are Reading!
Was this article worth reading? Share it with fellow developers too. Thanks!
Share on LinkedIn
Share on Google+

Author
Mahesh Sabnis is a DotNetCurry author and a Microsoft MVP having over two decades of experience in IT education and development. He is a Microsoft Certified Trainer (MCT) since 2005 and has conducted various Corporate Training programs for .NET Technologies (all versions), and Front-end technologies like Angular and React. Follow him on twitter @maheshdotnet or connect with him on LinkedIn


Page copy protected against web site content infringement 	by Copyscape




Feedback - Leave us some adulation, criticism and everything in between!
Comment posted by raja on Thursday, September 18, 2014 8:44 AM
USE [Re_Broker]
GO
/****** Object:  UserDefinedFunction [dbo].[Split]    Script Date: 09/18/2014 12:31:55 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
--select * from [dbo].[Split]('1,456,89,23,420,0,45',',')
ALTER FUNCTION [dbo].[Split](@String VARCHAR(8000), @Delimiter CHAR(1))      
RETURNS @temptable TABLE (Id INT, [Value] VARCHAR(200))      
AS      
BEGIN      
    DECLARE @idx INT,
         @Id INT      
    DECLARE @slice VARCHAR(8000)      

    SELECT @idx = 1, @Id = 1      
        
   IF LEN(@String) < 1 OR @String IS NULL  
      RETURN;      

    WHILE @idx != 0      
    BEGIN      
        SET @idx = CHARINDEX(@Delimiter,@String)      
        IF @idx != 0      
            SET @slice = LEFT(@String,@idx - 1)      
        ELSE      
            SET @slice = @String      

        IF(LEN(@slice)>0)  
            INSERT INTO @temptable(Id, [Value]) VALUES(@Id, @slice)      

        SET @String = RIGHT(@String,LEN(@String) - @idx)      
         IF LEN(@String) = 0
            break;

      SET @Id = @Id + 1;      
    END  
RETURN
END;