ASP.NET Chart with MVC and Google Spreadsheet API

Scott Guthrie recently posted about the New ASP.NET Charting Control: <asp:chart runat="server"/>.

This weekend I decided to give it a try and as always I had to go through the samples to learn how to assemble a chart.

To learn about the new charting controls I got the Microsoft Chart Controls Samples project and played with the contents of the Getting Started section.

At the same time I was translating to Portuguese ScottGu’s blog post about the ASP.NET MVC 1.0 Release Candidate. I was also updating a Google spreadsheet that I use to keep track of worked hours at Chemtech.

To learn how to integrate ASP.NET charting controls with ASP.NET MVC and Google Spreadsheet Data API I created a new ASP.NET MVC project and started coding a sample application.

The following is how I got it all together:

using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Web.Mvc;
using System.Web.UI.DataVisualization.Charting;
using Google.GData.Client;
using Google.GData.Extensions;
using Google.GData.Spreadsheets;
namespace MvcChartGoogleSpreadsheet.Views.Home
{
    /// <summary>
    /// ASP.NET MVC application + ASP.NET charting controls + Google Spreadsheet Data API web-based application
    /// It demonstrates the operations supported by each of these technologies.
    /// It requires authentication in the form of your Google Docs & Spreadsheets username
    /// and password, and retrieves data from a worksheet of your choice.
    /// A chart is created with the data acquired through a CellFeed query.
    /// </summary>
    public partial class Index : ViewPage
    {
        private List<WorksheetEntry> allWorksheets = new List<WorksheetEntry>();

        protected void Page_Load(object sender, System.EventArgs e)
        {
            // Calling the method that configures the chart.
            ConfigureChart();

            // Creating a Google SpreadsheetService object passing to it the name of the application.
            SpreadsheetsService service = new SpreadsheetsService("MvcChartGoogleSpreadsheet");

            // Google account information (login and password)
            service.setUserCredentials("username@gmail.com", "password");

            GetAllSpreadsheetsAndWorksheets(service);

            // Using LINQ query expression to get a specific worksheet.
            var entry = from wse in allWorksheets
                        where
                            wse.Title.Text == "2008 leniel.net" // This is the name of the worksheet.
                        select wse;

            // Demonstrate a CellFeed query.
            CellFeed cellFeed = GetWorksheetCellFeed(service, entry.First());

            // Each entry represents a cell within the worksheet.
            foreach(CellEntry cellEntry in cellFeed.Entries)
            {
                // I just want to get the contents of column 2 of the worksheet.
                // The value of the cell present in column 2 will be used in the X axis.
                if(cellEntry.Cell.Column == 2)
                {
                    // I get the value of column 7 (cellEntry.Cell.Column + 5) of the same row. This value will be used in the Y axis.
                    // I replace the colon present in the value with a dot. I do so to make the data valid for calculating values.
                    string yValue = ((CellEntry)cellFeed.Entries.SingleOrDefault(es => ((CellEntry)es).Cell.Row == cellEntry.Cell.Row && ((CellEntry)es).Cell.Column == cellEntry.Cell.Column + 5)).Cell.Value.Replace(":", ".");

                    // I then remove the extra data that isn't necessary at all in my case.
                    yValue = yValue.Remove(yValue.Length - 3, 3);

                    // I pass the X and Y values to create a Point used in the series of the chart.
                    chart1.Series["Hours of work"].Points.AddXY(cellEntry.Cell.Value, yValue);
                }
            }
        }

        private void ConfigureChart()
        {
            chart1.Series.Add("Hours of work");

            chart1.Titles.Add("My chart title");

            // Add header separator of type line      
            chart1.Legends["Default"].HeaderSeparator = LegendSeparatorStyle.Line;
            chart1.Legends["Default"].HeaderSeparatorColor = Color.Gray;

            // Add Color column      
            LegendCellColumn firstColumn = new LegendCellColumn();
            firstColumn.ColumnType = LegendCellColumnType.SeriesSymbol;
            firstColumn.HeaderText = "Color";
            firstColumn.HeaderBackColor = Color.WhiteSmoke;
            chart1.Legends["Default"].CellColumns.Add(firstColumn);

            // Add Legend Text column      
            LegendCellColumn secondColumn = new LegendCellColumn();
            secondColumn.ColumnType = LegendCellColumnType.Text;
            secondColumn.HeaderText = "Name";
            secondColumn.Text = "#LEGENDTEXT";
            secondColumn.HeaderBackColor = Color.WhiteSmoke;
            chart1.Legends["Default"].CellColumns.Add(secondColumn);

            // Add AVG cell column      
            LegendCellColumn avgColumn = new LegendCellColumn();
            avgColumn.Text = "#AVG{N2}";
            avgColumn.HeaderText = "Avg";
            avgColumn.Name = "AvgColumn";
            avgColumn.HeaderBackColor = Color.WhiteSmoke;
            chart1.Legends["Default"].CellColumns.Add(avgColumn);

            // Add Total cell column      
            LegendCellColumn totalColumn = new LegendCellColumn();
            totalColumn.Text = "#TOTAL{N1}";
            totalColumn.HeaderText = "Total";
            totalColumn.Name = "TotalColumn";
            totalColumn.HeaderBackColor = Color.WhiteSmoke;
            chart1.Legends["Default"].CellColumns.Add(totalColumn);

            // Set Min cell column attributes      
            LegendCellColumn minColumn = new LegendCellColumn();
            minColumn.Text = "#MIN{N1}";
            minColumn.HeaderText = "Min";
            minColumn.Name = "MinColumn";
            minColumn.HeaderBackColor = Color.WhiteSmoke;
            chart1.Legends["Default"].CellColumns.Add(minColumn);

            // Show labels at every 2 days
            chart1.ChartAreas["ChartArea1"].AxisX.LabelStyle.Interval = 2;
            chart1.ChartAreas["ChartArea1"].AxisX.MajorGrid.Interval = 2;
            chart1.ChartAreas["ChartArea1"].AxisX.MajorTickMark.Interval = 2;

            // Set series tooltips
            chart1.Series["Hours of work"].ToolTip = "#VALX";

// Set the width of the chart
           chart1.Width = 510;

           // Set legend docking
           chart1.Legends["Default"].Docking = Docking.Bottom;

           // Set legend alignment
           chart1.Legends["Default"].Alignment = StringAlignment.Center;
        }

        /// <summary>
        /// Gets a list of all the user's spreadsheets, and the
        /// list of worksheets that each spreadsheet contains.
        /// </summary>
        /// <param name="service">An authenticated SpreadsheetsService object</param>
        private void GetAllSpreadsheetsAndWorksheets(SpreadsheetsService service)
        {
            SpreadsheetQuery query = new SpreadsheetQuery();

            SpreadsheetFeed feed = service.Query(query);

            foreach(SpreadsheetEntry entry in feed.Entries)
            {
                GetAllWorksheets(service, entry);
            }
        }

        /// <summary>
        /// Gets a list of all worksheets in the specified spreadsheet.
        /// </summary>
        /// <param name="service">An authenticated SpreadsheetsService object</param>
        /// <param name="entry">The spreadsheet whose worksheets are to be retrieved</param>
        private void GetAllWorksheets(SpreadsheetsService service, SpreadsheetEntry entry)
        {
            AtomLink link = entry.Links.FindService(GDataSpreadsheetsNameTable.WorksheetRel, null);

            WorksheetQuery query = new WorksheetQuery(link.HRef.ToString());

            WorksheetFeed feed = service.Query(query);

            foreach(WorksheetEntry worksheet in feed.Entries)
            {
                allWorksheets.Add(worksheet);
            }
        }

        /// <summary>
        /// Performs a cell range query on the specified worksheet to
        /// retrieve only the cells contained within the specified range.
        /// </summary>
        /// <param name="service">An authenticated SpreadsheetsService object</param>
        /// <param name="entry">The worksheet to retrieve</param>
        private static CellFeed GetWorksheetCellFeed(SpreadsheetsService service, WorksheetEntry entry)
        {
            AtomLink listFeedLink = entry.Links.FindService(GDataSpreadsheetsNameTable.CellRel, null);

            CellQuery query = new CellQuery(listFeedLink.HRef.ToString());
            // Defining the range of cells that I want to be retrieved.
            query.Range = "B5:G29";

            CellFeed feed = service.Query(query);

            return feed;
        }
    }
}

The above code is commented so I don’t think it needs more words.

ASP.NET MVC
I’ve already written about ASP.NET MVC in a post titled Hello World Web Site with ASP.NET MVC. If you don’t know what MVC means, don’t panic! It’s just an architectural and design pattern that advocates a clean separation of concerns in software engineering.

To get the ASP.NET MVC I’d recommend the Web Platform Installer that Microsoft released not so long ago.

Just download and install the Web Platform Installer and select what web development tools you want to be installed on your machine.

MicrosoftWebPlatformInstaller

As you can see above I already have the ASP.NET MVC Release Candidate 1 installed on my machine. In case you don’t have it yet, select the checkbox and click the Install button. The Web Platform Installer will do the rest of the job, that is, it’ll download the packages and install it conveniently for you.

An interesting thing about the Web Platform Installer is that it’ll always have the most updated bits to be installed.

After installing the ASP.NET MVC you’re ready to create an ASP.NET MVC Web Application in Visual Studio 2008:

ASP.NETMvcChartGoogleSpreadsheetProject

Google Spreadsheet API
To accomplish the goal of this post I needed more documentation and so I went to Google Spreadsheets APIs and Tools page. From there I went to read the Google Spreadsheets Data API Developer's Guide. Specifically the Developer's Guide for .NET since I’m playing with Microsoft development platform.

After reading some bits here and some bits there I went directly to the Libraries and Code page and went to the download page of google-gdata – the .NET library for the Google Data API.

I downloaded the file Google Data API Setup(1.3.1.0).msi and installed it. It has sample projects for all Google Data APIs. I was interested in the Spreadsheet API and so I got the specific code and dropped it on the ASP.NET MVC project.

As you can see in the above code the using statements include 3 references to Google Data API DLLs:

using Google.GData.Client;
using Google.GData.Extensions;
using Google.GData.Spreadsheets;

These DLLs are part of Google Data API Setup(1.3.1.0).msi installation file and are located at C:\Program files\Google\Google Data API SDK\Redist in my machine. You’ll need to add these DLLs to the References folder of the ASP.NET MVC project.

ASP.NET Charting Controls

There are charts of all types:

- Bar and Column Charts
- Line Charts
- Area Charts
- Pie and Doughnut Charts
- Point Charts
- Range Charts
- Circular Charts
- Accumulation Charts
- Data Distribution Charts
- Price Change Financial Charts
- Advanced Financial Charts
- Combinational Charts

To get the charting controls in Visual Studio 2008 I downloaded 2 files:

- Microsoft Chart Controls for Microsoft .NET Framework 3.5
- Microsoft Chart Controls Add-on for Microsoft Visual Studio 2008

After installing both files, open Visual Studio 2008 and go to a webform/viewpage .ASPX page. Now in the Toolbox you’ll find the new Chart control.

ASP.NET Chart Control

Drag and drop the chart onto the page and you’re ready to go.

Look to the using statement present in the code behind page:

using System.Web.UI.DataVisualization.Charting;

This is the column chart I got when I ran the application:

ASP.NET Chart Imgage

LINQ
I used some constructs of LINQ in the above sample application. If you need further details about this fantastic technology: read my post LINQ - Language Integrated Query.

Note:
To work with Dates in a chart I had to go through the Microsoft Chart Controls Samples project and look at Working with Chart Data – Working with Dates – Setting Time Scale.

Summary
As you could see the ASP.NET charting control is feature rich and you can use it to show data from all sorts of sources as for example a Google spreadsheet. I just showed here the simplest of the charts.

Using the charting controls in an ASP.NET MVC application is a really straightforward task. Of course, you’d have action methods that’d generate the chart for you based on some logic you implement in such methods. The sample given here was just to exercise the integration of technologies available nowadays to build richer internet applications.

References
ASP.NET MVC
Hello World Web Site with ASP.NET MVC
ASP.NET MVC : The Official Microsoft ASP.NET Site
ScottGu's Blog
Model View Controller (MVC) at Wikipedia

Google Spreadsheet API
Google Spreadsheets APIs and Tools page
Google Spreadsheets Data API Developer's Guide
Google Spreadsheets Developer's Guide for .NET
Google Spreadsheets Libraries and Code page
google-gdata – the .NET library for the Google Data API

ASP.NET Charting controls
Download the Microsoft Chart Controls Documentation
Microsoft Chart Control Forum
Alex Gorev's Weblog - Data Visualization
Charting With ASP.NET And LINQ

Downloads
ASP.NET MVC
Web Platform Installer

Google Spreadsheet API
Google Data API Setup(1.3.1.0).msi

ASP.NET Charting controls
Microsoft Chart Controls for Microsoft .NET Framework 3.5
Microsoft Chart Controls Add-on for Microsoft Visual Studio 2008
Microsoft Chart Controls Samples project

Visual Studio 2008 C# ASP.NET MVC Web Application
You can get the Microsoft Visual Studio Project at:

http://leniel.googlepages.com/MvcChartGoogleSpreadsheet.zip

Note: Remember to change/substitute the username and password present in the file C:\MvcChartGoogleSpreadsheet\Views\Home\Index.aspx.cs so that the application can log you on to Google Spreadsheet service and get your data (spreadsheet along with the worksheets).