Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / web / HTML

Using ASP.NET Charting with Image Maps in MVC

4.73/5 (10 votes)
13 Dec 2011CPOL10 min read 81.7K   2.9K  
This article presents an example to create interactive charts on browsers using the ASP.NET charting tool with Image Maps. It also demonstrates how to add the same charts in PDF documents by re-using the same code.

Introduction

This article presents an example to create interactive charts on the browsers using the "ASP.NET charting" tool with "Image Maps". It also demonstrates how to add the same charts in PDF documents by re-using the same code.

Background

When developing web applications, you can easily find many tools that help to add charts to your applications. Most of these tools use client side JavaScript. Among these tools, you should easily notice the "jQuery based charting libraries" and the Telerik Charting Library. They either base the charts on HTML5 canvases or SVG Graphics. These charting libraries can bring highly advanced and interactive charts to web browsers. But sometimes common requirements in your projects may include:

  • Creating interactive charts on web browsers;
  • Displaying the same charts in other places, such as in Microsoft Word or PDF documents.

It is not an easy task to re-use the same client side JavaScript code to meet both requirements. In this article, I will try to meet both requirements using the ASP.NET Charting tool with Image Maps in MVC. Different from the previously mentioned charting libraries, the charts generated by the ASP.NET charting tool are images. These image charts can be as beautiful as charts created by other libraries, but they have limited ability to support user interactions on browsers. The example application in this article is to demonstrate to you the following:

  • When used with Image Maps, the charts created by the ASP.NET charting tool should provide some reasonable user interaction capabilities. The ASP.NET charting tool enables us to easily generate the "Image Maps" associated with the charting images. You will find out how to generate the Image Maps and associate them with the charting images from the example application.
  • Besides showing the charts on browsers, the example application will also show you how to use the exact same code to create charts to be embedded in PDF documents.

The attached simple MVC 2 web application is developed in Visual Studio 2010. Since it is developed as an MVC application, I will assume readers have some basic knowledge of how to use MVC.

SolutionExplorer.jpg

As shown in the Solution Explorer, the main building blocks of this web application are the following:

  • The Models\BrowserShareModel.cs file is the application's data model. We will use the data in this data model to generate the charts using the ASP.NET charting tool.
  • The two files MyChartBase.cs and BrowserShareChart.cs in the Utilities\ChartUtilities folder implement the classes that will be used to generate the charts. MyChartBase.cs defines the basic look and feel of the charts, while BrowserShareChart.cs inherits from the MyChartBase class and overrides some methods to insert data to the charts.
  • The Utilities\PdfUtility.cs file implements a simple class to generate a PDF document using the chart images created by the above two classes.
  • The Controllers\HomeController.cs file is this simple MVC application's controller and the Views\Home\Index.aspx file is the application's view.

In this article, I will first introduce you to the application's data model. I will then introduce the utility classes and show how to use these classes in the MVC application.

The Data Model

The application's data model is implemented in the Models\BrowserShareModel.cs file:

C#
using System.Collections.Generic;
using System.IO;
using MVCChart.Utilities.ChartUtilities;
 
namespace MVCChart.Models
{
    public class BrowserInformation
    {
        public string Name { get; set; }
        public double Share { get; set; }
        public string Url { get; set; }
        public string ToolTip
        {
            get
            {
                return Name + " " + Share.ToString("#0.##%");
            }
        }
    }
 
    public class BrowserShareChartData
    {
        public string Title { get; set; }
        public int Width { get; set; }
        public int Height { get; set; }
        public List<BrowserInformation> ShareData { get; set; }
 
        public MemoryStream ChartImageStream()
        {
            var chart = new BrowserShareChart(this);
            return chart.GetChartImage(Width, Height);
        }
 
        public string ChartImageMap(string name)
        {
            var chart = new BrowserShareChart(this);
            return chart.GetChartImageMap(Width, Height, name);
        }
    }
 
    public class BrowserShareRepository
    {
        public static BrowserShareChartData GetBrowserShares()
        {
            var chartData = new BrowserShareChartData()
                                {
                                    Title = "Browser usage on Wikipedia October 2011",
                                    Width = 450,
                                    Height = 300,
                                    ShareData = new List<BrowserInformation>()
                                };
 
            // The following data is the true data from Wikipedia
            chartData.ShareData.Add(new BrowserInformation()
                           {
                               Name = "IE",
                               Share = 0.342,
                               Url = "http://en.wikipedia.org/wiki/Internet_Explorer"
                           });
 
            chartData.ShareData.Add(new BrowserInformation()
                           {
                               Name = "Firefox",
                               Share = 0.236,
                               Url = "http://en.wikipedia.org/wiki/Firefox"
                           });
 
            chartData.ShareData.Add(new BrowserInformation()
                           {
                               Name = "Chrome",
                               Share = 0.206,
                               Url = "http://en.wikipedia.org/wiki/Google_Chrome"
                           });
 
            chartData.ShareData.Add(new BrowserInformation()
                           {
                               Name = "Safari",
                               Share = 0.112,
                               Url = "http://en.wikipedia.org/wiki/Safari_(web_browser)"
                           });
 
            chartData.ShareData.Add(new BrowserInformation()
                           {
                               Name = "Other",
                               Share = 0.104,
                               Url = null
                           });
 
            return chartData;
        }
    }
}

This file implements three classes:

  • BrowserInformation defines a data point in the chart.
  • The BrowserShareChartData class defines the data for the chart. It holds a reference of a "List" of "BrowserInformation" objects to create the chart "Series". It also defines the width, height, and the title of the chart.
  • The BrowserShareRepository class instantiates a BrowserShareChartData object and assigns it some real data about the browser market share in October 2011 that I got from Wikipedia.

In this article, I will show you how to create a chart using the ASP.NET Charting tool to visualize the browser market share information. The chart will be shown on the browser and in a PDF document. When shown in a browser, I will add tooltips and hyperlinks to the chart using an Image Map to make the chart interactive. You should be able to create more advanced interactions using Image Maps, but I will only show you how to add tooltips and hyperlinks to keep this article simple.

The Charting Utility Classes

"Utilities\ChartUtilities\MyChartBase.cs" defines the basic look and feel of the charts in the example application:

C#
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Web.UI.DataVisualization.Charting;
 
namespace MVCChart.Utilities.ChartUtilities
{
    public class MyChartBase
    {
        protected List<Series> ChartSeriesData { get; set; }
        protected string ChartTitle { get; set; }
 
        // This is the method to get the chart image
        public MemoryStream GetChartImage(int width, int height)
        {
            var chart = InitiateChart(width, height);
            chart.RenderType = RenderType.BinaryStreaming;
 
            var ms = new MemoryStream();
            chart.SaveImage(ms, ChartImageFormat.Png);
 
            return ms;
        }
 
        // This is the method to get the chart image map
        public string GetChartImageMap(int width, int height, string mapName)
        {
            var chart = InitiateChart(width, height);
            chart.RenderType = RenderType.ImageMap;
            chart.SaveImage(Stream.Null);
 
            return chart.GetHtmlImageMap(mapName);
        }
 
        // Override this method to add title to the chart
        protected virtual void AddChartTitle()
        {
            ChartTitle = null;
        }
 
        // Override this method to add data to the chart
        protected virtual void AddChartSeries()
        {
            ChartSeriesData = new List<Series>();
        }
 
        // Initiate the chart to be rendered
        private Chart InitiateChart(int width, int height)
        {
            var chart = new Chart();
            chart.Width = width;
            chart.Height = height;
            chart.BorderSkin.BackColor = System.Drawing.Color.Transparent;
            chart.BorderSkin.PageColor = System.Drawing.Color.Transparent;
            chart.BackColor = System.Drawing.Color.FromArgb(211, 223, 240);
            chart.BorderlineDashStyle = ChartDashStyle.Solid;
            chart.BackSecondaryColor = System.Drawing.Color.White;
            chart.BackGradientStyle = GradientStyle.TopBottom;
            chart.BorderlineWidth = 1;
            chart.Palette = ChartColorPalette.BrightPastel;
            chart.BorderlineColor = System.Drawing.Color.FromArgb(26, 59, 105);
            chart.BorderSkin.SkinStyle = BorderSkinStyle.Emboss;
            chart.AntiAliasing = AntiAliasingStyles.All;
            chart.TextAntiAliasingQuality = TextAntiAliasingQuality.Normal;
 
            AddChartTitle();
            if (ChartTitle != null)
            {
                chart.Titles.Add(CreateTitle());
            }
            chart.Legends.Add(CreateLegend());
 
            AddChartSeries();
            foreach (var series in ChartSeriesData)
            {
                chart.Series.Add(series);
            }
             
            chart.ChartAreas.Add(CreateChartArea());
            return chart;
        }
 
        // Create chart title
        private Title CreateTitle()
        {
            return new Title()
                       {
                           Text = ChartTitle,
                           ShadowColor = System.Drawing.Color.FromArgb(32, 0, 0, 0),
                           Font = new System.Drawing.Font("Trebuchet MS", 10, FontStyle.Bold),
                           ShadowOffset = 3,
                           ForeColor = System.Drawing.Color.FromArgb(26, 59, 105)
                       };
        }
 
        // configure chart Legend
        private Legend CreateLegend()
        {
            return new Legend()
            {
                Docking = Docking.Bottom,
                Alignment = StringAlignment.Center,
                BackColor = System.Drawing.Color.Transparent,
                Font = new System.Drawing.Font(new System.Drawing.FontFamily("Trebuchet MS"), 8),
                LegendStyle = LegendStyle.Row
            };
        }
 
        // Configure the chart area - the chart frame x/y axes
        private ChartArea CreateChartArea()
        {
            var area = new ChartArea()
                           {
                               Name = ChartTitle,
                               BackColor = System.Drawing.Color.Transparent,
                           };
 
            area.AxisX.IsLabelAutoFit = true;
            area.AxisX.LabelStyle.Font =
                new System.Drawing.Font("Verdana,Arial,Helvetica,sans-serif",
                                        8F, FontStyle.Regular);
            area.AxisX.LineColor = System.Drawing.Color.FromArgb(64, 64, 64, 64);
            area.AxisX.MajorGrid.LineColor = System.Drawing.Color.FromArgb(64, 64, 64, 64);
            area.AxisX.Interval = 1;
 
 
            area.AxisY.LabelStyle.Font =
                new System.Drawing.Font("Verdana,Arial,Helvetica,sans-serif",
                                        8F, FontStyle.Regular);
            area.AxisY.LineColor = System.Drawing.Color.FromArgb(64, 64, 64, 64);
            area.AxisY.MajorGrid.LineColor = System.Drawing.Color.FromArgb(64, 64, 64, 64);
            
            return area;
        }
    }
}

If you create an object of this class and call the GetChartImage method, you will get the MemoryStream of an empty chart image. If you call the GetChartImageMap method, you will get the empty HTML string of the Image Map associated with the chart image. I will not go to the details of how to use the ASP.NET Charting tool here. But if you are interested, you can take a look at the article ASP.NET MVC Chart Control. It is the place where I got started to use the ASP.NET Charting tool in the MVC environment.

To visualize your data in a meaningful chart, we need to create a new class that inherits from the MyChartBase class and override the AddChartSeries and AddChartTitle methods to add the data to the chart. The following is the class to create the browser market share chart in this article:

C#
using System.Collections.Generic;
using System.Web.UI.DataVisualization.Charting;
using MVCChart.Models;
 
namespace MVCChart.Utilities.ChartUtilities
{
    // This class built the browser share chart using the "ChartBase" class
    public class BrowserShareChart : MyChartBase
    {
        private BrowserShareChartData chartData;
 
        public BrowserShareChart(BrowserShareChartData chartData)
        {
            this.chartData = chartData;
        }
 
        // Add Chart Title
        protected override void AddChartTitle()
        {
            ChartTitle = chartData.Title;
        }
 
        // Override the AddChartSeries method to provide the chart data
        protected override void AddChartSeries()
        {
            ChartSeriesData = new List<Series>();
            var series = new Series()
            {
                ChartType = SeriesChartType.Pie,
                BorderWidth = 1
            };
 
            var shares = chartData.ShareData;
            foreach (var share in shares)
            {
                var point = new DataPoint();
                point.IsValueShownAsLabel = true;
                point.AxisLabel = share.Name;
                point.ToolTip = share.Name + " " + 
                      share.Share.ToString("#0.##%");
                if (share.Url != null)
                {
                    point.MapAreaAttributes = "href=\"" + 
                          share.Url + "\"";
                }
                point.YValues = new double[] { share.Share };
                point.LabelFormat = "P1";
                series.Points.Add(point);
            }
 
            ChartSeriesData.Add(series);
        }
    }
}

By inheriting from the MyChartBase class, the BrowserShareChart class inherits the styles defined in MyChartBase. To add the data for visualization:

  • It overrides the AddChartTitle method to add a title to the chart.
  • It also overrides the AddChartSeries method to add the "Series" data to the chart.

You should pay some attention to the AddChartSeries method. Besides adding chart data, it also adds tooltips and hyperlinks to the chart "Series". If an object of BrowserShareChart is properly initiated with the correct data, you can call the GetChartImage method inherited from the base class to obtain the chart image in the form of a MemoryStream. You can also call GetChartImageMap in the base class to obtain the Image Map that adds the tooltips and hyperlinks to your chart in the browsers. I will show you how to use this Image Map later in this article.

The PdfUtility Class

In order to show you how to add charts in PDF documents, I created a small class in the "Utilities\PdfUtility.cs" file:

C#
using System.IO;
using iTextSharp.text;
using iTextSharp.text.pdf;
 
namespace MVCChart.Utilities
{
    public class PdfUtility
    {
        // Create a simple Pdf document and add an image to it.
        public static MemoryStream GetSimplePdf(MemoryStream chartImage)
        {
            const int documentMargin = 10;
 
            var pdfStream = new MemoryStream();
            var pdfDocument = new Document(PageSize.LETTER);
            pdfDocument.SetMargins(documentMargin, documentMargin, 
                                   documentMargin, documentMargin);
            PdfWriter pdfWriter = PdfWriter.GetInstance(pdfDocument, pdfStream);
 
            Image image = Image.GetInstance(chartImage.GetBuffer());
            image.SetAbsolutePosition(documentMargin
                , pdfDocument.PageSize.Height - documentMargin - image.ScaledHeight);
 
            pdfDocument.Open();
            pdfDocument.Add(image);
            pdfDocument.Close();
            pdfWriter.Flush();
 
            return pdfStream;
        }
    }
}

This small class is used to generate a very simple PDF document. If you pass the MemoryStream for the chart generated by the BrowserShareChart class to the GetSimplePdf method, it will return to you a MemoryStream of a PDF document with the chart in it. This class uses iTextSharp to create the PDF document. You can download iTextSharp from its web site. I am not going to the details of how to use iTextSharp here. If you are interested, you can take a look at the article Export Tabular Data in PDF Format Through the Web. It has detailed instructions on how to use iTextSharp.

Now that we have completed the data model and the utility classes, let us take a look at how we can use them in our MVC application.

The MVC Controller

The MVC application's controller is implemented in the Controllers\HomeController.cs file:

C#
using System.Web.Mvc;
using MVCChart.Models;
using MVCChart.Utilities;
 
namespace MVCChart.Controllers
{
    [HandleError]
    public class HomeController : Controller
    {
        [HttpGet]
        public ActionResult Index()
        {
            var chartData = BrowserShareRepository.GetBrowserShares();
            return View(chartData);
        }
 
        [HttpGet]
        public FileResult GetChart()
        {
            var chartData = BrowserShareRepository.GetBrowserShares();
            return File(chartData.ChartImageStream().GetBuffer()
                , @"image/png", "BrowserShareChart.png");
        }
 
        [HttpGet]
        public FileResult GetPdf()
        {
            var chartData = BrowserShareRepository.GetBrowserShares();
            var chartStream = chartData.ChartImageStream();
 
            return File(PdfUtility.GetSimplePdf(chartStream).GetBuffer()
                , @"application/pdf", "BrowserShareChart.pdf");
        }
    }
}

The controller of this MVC application has three "Action" methods:

  • The Index method is the application's entry point that brings up the view page of the web application.
  • The GetChart method calls the application's data model and indirectly calls GetChartImage in the BrowserShareChart class to obtain the MemoryStream of the browser market share chart image. It then passes the chart image to the browser as a FileResult.
  • The GetPdf method goes one step further. When it obtains the MemoryStream of the browser market share chart image, it passes it to the GetSimplePdf method in the PdfUtility class to obtain the PDF document. It also passes the PDF document to the browser as a "FileResult".

The MVC View

The MVC application's view is implemented in the "Views\Home\Index.aspx" file:

ASP.NET
<%@ Page Language="C#" Inherits="System.Web.Mvc.ViewPage<MVCChart.Models.BrowserShareChartData>" %>
 
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
 
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>Index</title>
    <link href="<%=Url.Content("~/content/Site.css") %>" rel="stylesheet" type="text/css" />
</head>
 
<body>
    <div style="display: inline-block">
        <div>
            <img usemap="#ChartImageMap" src="<%= Url.Action("GetChart", "Home") %>"
                alt="Asp.Net Charting generated image"
                style="width:450px;height:300px;border-width:0px;" />
            <%  Response.Write(Model.ChartImageMap("ChartImageMap"));%>
        </div>
        <div style="margin-right: 20px; text-align: right">
            <a href="<%= Url.Action("GetPdf", "Home") %>">Get the chart in Pdf</a>
        </div>
    </div>
</body>
</html>

In this simple view page, I added the following:

  • The chart image in an "img" tag and its "Image Map".
  • I also added a link to call the GetPdf method in the controller to download the PDF document.

To add an Image Map to the chart, we can simply write the Image Map created by the GetChartImageMap method in the BrowserShareChart class next to the img tag where we put our chart image. Associating the Image Map with the chart image is pretty simple, but you need to make sure that the name of the Image Map matches the usemap attribute of the image.

Run the Application

We have completed this example application, we can now test run it. When the application starts, the browser market share pie-chart is shown in the web browser.

RunApp.gif

If we move the mouse over the chart, the "image map" takes effect, and we can see the tooltip that we specified in the chart.

RunAppToolTip.gif

If we click on one of the sections in the chart, the corresponding web page is shown according to the hyperlink that we put in the chart.

RunAppHyperlink.gif

Click the back button on the browser to go back to our web page and click on the "Get the chart in PDF" link, a PDF document is generated with the same chart as the one shown in the browser.

RunAppPdf.gif

Points of Interest

  • This article presented an example to create interactive charts on browsers using the ASP.NET charting tool with Image Maps. It also demonstrated how to add the same charts in PDF documents by re-using the same code.
  • There are many charting libraries and most of them can create more efficient and more interactive charts on browsers than the ASP.NET charting tool. But they are dedicated to creating charts on browsers, so it is difficult to use the same charts on other places. Of course, if your only intention is to display your charts on browsers, I would recommend you use client side charting libraries like the Telerik Charting Library, which can definitely provide better interactivity and possibly better efficiency.
  • When used with Image Maps, the ASP.NET charting tool can create reasonably interactive charts on browsers. In this example, I only showed you how to add tooltips and hyperlinks to charts, but you can definitely make the charts more interactive by working on MapAreaAttributes when you add the chart "Series". You can also add client side JavaScript to make your charts more interactive.
  • Besides creating charts beyond browsers, the ASP.NET charting tool has some other advantages. In any foreseeable future, we can safely assume that images are always supported anywhere on any Operating System, so your charts should always be supported if you create them as images.
  • Another advantages to using ASP.NET charting is that it is now included in the default deployment of the .NET Framework. If .NET is your primary working environment, you do not need to add any extra DLLs in your projects to create your desired charts.
  • If you are developing a desktop application, you should also be able to use the ASP.NET charting tool to create charts and display them in your application.
  • Although the example is developed as an MVC application, you can easily use the same principle to add charts in more traditional ASP.NET applications.
  • I hope you like my postings and I hope this article can help you one way or the other.

History

  • 12/13/2011: First revision.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)