Charts are an important aspect of reports as they make it simpler to understand a certain problem. Making charts clickable adds more reinforcement into the understanding of the problem.
Today, I shall be demonstrating clickable charts using Google Charts API in ASP.NET MVC5. Google Charts API is simple to use and provides a variety of options for customization of graphical chart reports for better analytical purposes.
Prerequisites
Following are some prerequisites before you proceed further in this tutorial.
- Knowledge of Google charts API.
- Knowledge of ASP.NET MVC5.
- Knowledge of HTML.
- Knowledge of JavaScript.
- Knowledge of AJAX.
- Knowledge of CSS.
- Knowledge of Bootstrap.
- Knowledge of C# programming.
- Knowledge of C# LINQ.
- Knowledge of jQuery.
You can download the complete source code for this tutorial or you can follow the step by step discussion below. The sample code is developed in Microsoft Visual Studio 2015 Enterprise. I am using SalesOrderDetail table extracted from Adventure Works Sample Database.
Let's begin now.
Step 1
Create a new MVC5 web application project and name it "Graphs".
Step 2
Open "Views\Shared\_Layout.cshtml" file and replace the following code in it.
- <!DOCTYPE html>
- <html>
- <head>
- <meta charset="utf-8" />
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <title>@ViewBag.Title</title>
- @Styles.Render("~/Content/css")
- @Scripts.Render("~/bundles/modernizr")
-
-
- <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.4.0/css/font-awesome.min.css" />
-
- @* Custom *@
- @Styles.Render("~/Content/css/custom-style")
- </head>
- <body>
- <div class="navbar navbar-inverse navbar-fixed-top">
- <div class="container">
- <div class="navbar-header">
- <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
- <span class="icon-bar"></span>
- <span class="icon-bar"></span>
- <span class="icon-bar"></span>
- </button>
- </div>
- </div>
- </div>
- <div class="container body-content">
- @RenderBody()
- <hr />
- <footer>
- <center>
- <p><strong>Copyright © @DateTime.Now.Year - <a href="http://www.asmak9.com/">Asma's Blog</a>.</strong> All rights reserved.</p>
- </center>
- </footer>
- </div>
-
- @Scripts.Render("~/bundles/jquery")
- @Scripts.Render("~/bundles/bootstrap")
-
-
- <script type="text/javascript" src="https://www.google.com/jsapi"></script>
- @Scripts.Render("~/bundles/Script-custom-graphs")
-
- @RenderSection("scripts", required: false)
- </body>
- </html>
In the above code, I have simply created the basic layout structure of this web project and I have also add reference to the Google charts API.
Step 3
Create a new "Models\HomeViewModels.cs" file and replace with the following code in it.
- using System.Collections.Generic;
- using System.ComponentModel.DataAnnotations;
-
- namespace Graphs.Models
- {
- public class SalesOrderDetail
- {
- public int Sr { get; set; }
- public string OrderTrackNumber { get; set; }
- public int Quantity { get; set; }
- public string ProductName { get; set; }
- public string SpecialOffer { get; set; }
- public double UnitPrice { get; set; }
- public double UnitPriceDiscount { get; set; }
- public string Link { get; set; }
- }
- }
In the above code, we have simply created our View Model which will map the data from text file into main memory as object. Also, notice that we have added "Link" property, which we will use as our clickable destination.
Step 4
Now, create "Controllers\HomeController.cs" file and replace the following code in it.
- using Graphs.Models;
- using System;
- using System.Collections.Generic;
- using System.IO;
- using System.Linq;
- using System.Reflection;
- using System.Web;
- using System.Web.Mvc;
-
- namespace Graphs.Controllers
- {
- public class HomeController : Controller
- {
- #region Index method
-
-
-
-
-
- public ActionResult Index()
- {
-
- return this.View();
- }
-
- #endregion
-
- #region Test Page method
-
-
-
-
-
- public ActionResult TestPage()
- {
-
- return this.View();
- }
-
- #endregion
-
- #region Get data method.
-
-
-
-
-
- public ActionResult GetData()
- {
-
- JsonResult result = new JsonResult();
-
- try
- {
-
- List<SalesOrderDetail> data = this.LoadData();
-
-
- var graphData = data.GroupBy(p => new
- {
- p.ProductName,
- p.Link,
- p.UnitPrice
- })
- .Select(g => new
- {
- g.Key.ProductName,
- g.Key.Link,
- g.Key.UnitPrice
- }).OrderByDescending(q => q.UnitPrice).ToList();
-
-
- graphData = graphData.Take(2).Select(p => p).ToList();
-
-
- result = this.Json(graphData, JsonRequestBehavior.AllowGet);
- }
- catch (Exception ex)
- {
-
- Console.Write(ex);
- }
-
-
- return result;
- }
-
- #endregion
-
- #region Helpers
-
- #region Load Data
-
-
-
-
-
- private List<SalesOrderDetail> LoadData()
- {
-
- List<SalesOrderDetail> lst = new List<SalesOrderDetail>();
-
- try
- {
-
- string line = string.Empty;
- string srcFilePath = "Content/files/SalesOrderDetail.txt";
- var rootPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().CodeBase);
- var fullPath = Path.Combine(rootPath, srcFilePath);
- string filePath = new Uri(fullPath).LocalPath;
- StreamReader sr = new StreamReader(new FileStream(filePath, FileMode.Open, FileAccess.Read));
-
-
- while ((line = sr.ReadLine()) != null)
- {
-
- SalesOrderDetail infoObj = new SalesOrderDetail();
- string[] info = line.Split(',');
-
-
- infoObj.Sr = Convert.ToInt32(info[0].ToString());
- infoObj.OrderTrackNumber = info[1].ToString();
- infoObj.Quantity = Convert.ToInt32(info[2].ToString());
- infoObj.ProductName = info[3].ToString();
- infoObj.SpecialOffer = info[4].ToString();
- infoObj.UnitPrice = Convert.ToDouble(info[5].ToString());
- infoObj.UnitPriceDiscount = Convert.ToDouble(info[6].ToString());
- infoObj.Link = this.Url.Action("TestPage", "Home");
-
-
- lst.Add(infoObj);
- }
-
-
- sr.Dispose();
- sr.Close();
- }
- catch (Exception ex)
- {
-
- Console.Write(ex);
- }
-
-
- return lst;
- }
-
- #endregion
-
- #endregion
- }
- }
In the above code, I have created a simple index() & TestPage() action methods along with a helper method LoadData() for data loading from text file and finally, GetData() action method which will be called by Google charts API AJAX method in order to map the data on the chart. The GetData() action method will return top the top two rows only which are sorted by product and unit price and grouped by product name.
Step 5
Create a new "Scripts\script-custom-graphs.js" script file and replace the following code in it.
-
- google.load('visualization', '1.0', { 'packages': ['corechart'] });
-
-
- $(document).ready(function ()
- {
- $.ajax(
- {
- type: 'POST',
- dataType: 'JSON',
- url: '/Home/GetData',
- success:
- function (response)
- {
-
- var options =
- {
- width: 1100,
- height: 900,
- sliceVisibilityThreshold: 0,
- legend: { position: "top", alignment: "end" },
- chartArea: { left: 370, top: 50, height: "90%" },
-
- bar: { groupWidth: "50%" },
- };
-
-
- drawGraph(response, options, 'graphId');
- }
- });
- });
-
-
-
-
- function drawGraph(dataValues, options, elementId) {
-
- var data = new google.visualization.DataTable();
-
-
- data.addColumn('string', 'Product Name');
- data.addColumn('number', 'Unit Price');
- data.addColumn('string', 'Link');
-
-
- for (var i = 0; i < dataValues.length; i++)
- {
-
- data.addRow([dataValues[i].ProductName, dataValues[i].UnitPrice, dataValues[i].Link]);
- }
-
-
- var view = new google.visualization.DataView(data);
- view.setColumns([0, 1,
- {
- calc: "stringify",
- sourceColumn: 1,
- type: "string",
- role: "annotation"
- }
- ]);
-
-
- var chart = new google.visualization.ColumnChart(document.getElementById(elementId));
-
-
- chart.draw(view, options);
-
-
- var selectHandler = function (e)
- {
-
- if (chart.getSelection() != null &&
- chart.getSelection()[0] != null &&
- chart.getSelection()[0]['row'] != null &&
- chart.getSelection().length > 0)
- {
- if (chart.getSelection()[0]['column'] == 1)
- {
-
- var link = data.getValue(chart.getSelection()[0]['row'], 2)
- window.open(link, '_blank');
- }
- }
- }
-
-
- google.visualization.events.addListener(chart, 'select', selectHandler);
- }
Let's break down the code chunk by chunk. First, I have loaded the Google Charts API charts visualization package.
-
- google.load('visualization', '1.0', { 'packages': ['corechart'] });
Then, I call the GetData() server side method via AJAX call and after successfully receiving the data, I simply set the default chart options then pass those options to a user-define JavaScript method "drawGraph(...)".
-
- $(document).ready(function ()
- {
- $.ajax(
- {
- type: 'POST',
- dataType: 'JSON',
- url: '/Home/GetData',
- success:
- function (response)
- {
-
- var options =
- {
- width: 1100,
- height: 900,
- sliceVisibilityThreshold: 0,
- legend: { position: "top", alignment: "end" },
- chartArea: { left: 370, top: 50, height: "90%" },
- hAxis:
- {
- slantedText: true,
- slantedTextAngle: 18
- },
- bar: { groupWidth: "50%" },
- };
-
-
- drawGraph(response, options, 'graphId');
- }
- });
- });
Now, in the below drawGraph(...) method code, I add three new columns per row, the zero column will be the name of the products which will be shown on the chart axis, the first column will be the unit price of the product which will be shown on the graph for each product. After adding the column metadata for the chart, I will convert the received data from the server into DataTables data type accepted by the chart. Then I will set the annotation option for the first chart column which will display the correspondent values on the chart columns per each product. Then, I will draw the ColumnChart by calling Google charts API method. Next, I add the selection handler which will map target link to the target chart display column. Finally, I add the listener event which will call our selection handler method and open the link in a new window as we have coded in the selection handler; i.e.:
-
-
-
- function drawGraph(dataValues, options, elementId) {
-
- var data = new google.visualization.DataTable();
-
-
- data.addColumn('string', 'Product Name');
- data.addColumn('number', 'Unit Price');
- data.addColumn('string', 'Link');
-
-
- for (var i = 0; i < dataValues.length; i++)
- {
-
- data.addRow([dataValues[i].ProductName, dataValues[i].UnitPrice, dataValues[i].Link]);
- }
-
-
- var view = new google.visualization.DataView(data);
- view.setColumns([0, 1,
- {
- calc: "stringify",
- sourceColumn: 1,
- type: "string",
- role: "annotation"
- }
- ]);
-
-
- var chart = new google.visualization.ColumnChart(document.getElementById(elementId));
-
-
- chart.draw(view, options);
-
-
- var selectHandler = function (e)
- {
-
- if (chart.getSelection() != null &&
- chart.getSelection()[0] != null &&
- chart.getSelection()[0]['row'] != null &&
- chart.getSelection().length > 0)
- {
- if (chart.getSelection()[0]['column'] == 1)
- {
-
- var link = data.getValue(chart.getSelection()[0]['row'], 2)
- window.open(link, '_blank');
- }
- }
- }
-
-
- google.visualization.events.addListener(chart, 'select', selectHandler);
- }
Step 6
Create "Views\Home\_ViewGraphPartial.cshtml" & "Views\Home\Index.cshtml" files and replace following code in it.
Views\Home\_ViewGraphPartial.cshtml
- <section>
- <div class="well bs-component">
- <div class="row">
- <div class="col-xs-12">
-
- <div class="box box-primary">
- <div class="box-header with-border">
- <h3 class="box-title custom-heading">Product wise Graph</h3>
- </div>
- <div class="box-body">
- <div class="chart">
- <div id="graphId" style="width: 1100px; height: 900px; margin:auto;"></div>
- </div>
- </div>
- </div>
- </div>
- </div>
- </div>
- </section>
View\Home\Index.cshtml
- @{
- ViewBag.Title = "ASP.NET MVC5 - Clickable Google Charts";
- }
-
- <div class="row">
- <div class="panel-heading">
- <div class="col-md-8 custom-heading3">
- <h3>
- <i class="fa fa-pie-chart"></i>
- <span>ASP.NET MVC5 - Clickable Google Charts</span>
- </h3>
- </div>
- </div>
- </div>
-
- <div class="row">
- <section class="col-md-12 col-md-push-0">
- @Html.Partial("_ViewGraphPartial")
- </section>
- </div>
In the above code, I have simply created the view code for the page which will display the chart. I have divided the page into two parts for better manageability.
Step 7
Now, create new "Views\Home\TestPage.cshtml" file and replace following code in it.
- @{
- ViewBag.Title = "ASP.NET MVC5 - Clickable Google Charts";
- }
-
- <div class="row">
- <div class="panel-heading">
- <div class="col-md-8 custom-heading3">
- <h3>
- <i class="fa fa-pie-chart"></i>
- <span>ASP.NET MVC5 - Clickable Google Charts</span>
- </h3>
- </div>
- </div>
- </div>
-
- <div class="row">
- <section class="col-md-12 col-md-push-0">
- <div class="well bs-component">
- <div class="row">
- <div class="col-xs-12">
- <h1>Hello! I am Clickable Test Page</h1>
- </div>
- </div>
- </div>
- </section>
- </div>
In the above code, I have simply added a new page structure for the chart click event target.
Step 8
Execute the project and you will be able to see the following.
Conclusion
In this article, we have learned how to make charts clickable using Google Charts. We also learned to integrate Google charts API into asp.net mvc5 project.