In the past, I blogged about using Google Visualization to render charts with ASP.NET MVC here. It's pretty easy, it works, and fast. What if you do not want to use Google Visualization / javascripts to make your charts? Well, Microsoft released ASP.NET Charting Controls late 2008 and this blog post will discuss several options you can have in using it with ASP.NET MVC.
There are several ways you can do this (that I have tried/used):
- Generating and rendering the chart in the controller and returning it as a FileResult using MemoryStream
- Generating the chart in the controller and put it in the view data and let the aspx/ascx renders it using the webform control
There are advantages and disadvantages for both approaches. Using the MemoryStream approach works everytime, but it does not render the mapping, so it does not attach url, tooltip, etc on the legend and the chart by default. You have to do those things manually.
Now using the webform control gives you all the bells and whistles (url, tooltip, label, etc) - no extra work needed.
BUT - in my experience, it does not work if you run your MVC project under a virtual path. For example, if you run your project under root, such as
http://localhost:2000/Home/Index - it will work. But if you then go to the Project Property window in the solution explorer in VS 2008, go the the "Web" tab, and set a virtual path, and build and run (i.e. http://localhost:2000/Chart/Home/Index) - it will break. When it runs, it will give you "
Failed to map the path '/ChartImg.axd'". I am still unable to solve this problem yet - you can see my StackOverflow question
here.
Before I show you my code, I have to give credit to
Mike Ceranski - he
blogged about how to use the webform control in rendering ASP.NET chart - in which his approach become the one I used in my projects and become a partial subject of this blog post.
You will need to download a couple of things to get started:
First, you will need to prepare your web.config by adding these lines:
....
<appsettings>
<add key="RenderMode" value="MEMORYSTREAM" />
<add key="ChartImageHandler" value="privateImages=false;storage=file;timeout=20;deleteAfterServicing=false;url=/App_Data/;WebDevServerUseConfigSettings=true;"/>
</appSettings>
....
<system.web>
<httphandlers>
<add verb="*" path="ChartImg.axd" type="System.Web.UI.DataVisualization.Charting.ChartHttpHandler, System.Web.DataVisualization, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" validate="false"/>
</httpHandlers>
</system.web>
....
In your development environment, when running using VS 2008 by hitting F5, the images will always be rendered in memory regardless what you put in the appSettings, unless you are including the
"WebDevServerUseConfigSettings=true;" in there.
You can also read more about the possible enumerations and values for the appSettings
here.
Now, the HTML:
<%
if (ConfigurationManager.AppSettings["RenderMode"] == "MEMORYSTREAM") {
Response.Write("
");
} else {
myChart.Controls.Add(ViewData["Chart"] as Chart);
}
%>
The controller code:
public ActionResult Index() {
ViewData["Message"] = "Welcome to ASP.NET MVC!";
if (ConfigurationManager.AppSettings["RenderMode"] != "MEMORYSTREAM") {
Chart myChart = CreateChart();
ViewData["Chart"] = myChart;
}
return View();
}
public ActionResult Chart() {
using (var ms = new MemoryStream()) {
Chart myChart = CreateChart();
myChart.SaveImage(ms, ChartImageFormat.Png);
ms.Seek(0, SeekOrigin.Begin);
return File(ms.ToArray(), "image/png", "mychart.png");
}
}
private Chart CreateChart() {
Chart chart = new Chart();
chart.Width = 350;
chart.Height = 300;
chart.Attributes.Add("align", "left");
chart.Titles.Add("MY CHART"); // Display a Title
chart.ChartAreas.Add(new ChartArea());
chart.Series.Add(new Series());
chart.Legends.Add(new Legend("MY CHART"));
chart.Legends[0].TableStyle = LegendTableStyle.Auto;
chart.Legends[0].Docking = Docking.Bottom;
chart.Series[0].ChartType = SeriesChartType.Pie;
for (int i = 0; i < 10; i++) {
string x = "MEMBER " + (i + 1).ToString();
decimal y = ChartTest.Models.Utility.RandomNumber(1, 100);
int memberId = i;
int point = chart.Series[0].Points.AddXY(x, y);
DataPoint dPoint = chart.Series[0].Points[point];
dPoint.Url = "/Member/Detail/" + memberId.ToString();
dPoint.ToolTip = x + ": #VALY";
dPoint.LegendText = "#VALX: #VALY";
dPoint.LegendUrl = "/Member/Detail/" + memberId.ToString();
dPoint.LegendToolTip = "Click to view " + x + "'s information...";
}
chart.Series[0].Legend = "MY CHART";
return chart;
}
Now, depending on how your "RenderMode" setting is in the web.config, it will either render the chart using approach #1 (memory stream) or #2 (using webform control).
Additional readings/resources:
-- read more and comment ...