I borrowed a lot of chart formatting code from Michael Ceranski, so many thanks to him and his post.
-==- Microsoft Chart Control -==-
For .Net framework 3.5 you have to download Chart control from here.
If you're using .Net 4.0 Beta/Release Candidate (just released), it should be included within.
-==- Interface -==-
I wanted to use a simple interface that can use any IEnumerable<T> as data.
Sample interface usage:
Html.Chart<datatype>(IEnumerable<datatype>)
// general parameters
.Height(...)
.Width(...)
// data mapping and formatting
.Pie(
// title
"Pie Title",
// y-value from IQueryable
s => s.Value,
// expose DataPoint configuration
(dp, datatype) => {
// Set the legend
dp.LegendText = datatype.Name;
// Set the tooltip
dp.ToolTip = "Value for " + datatype.Name;
}
)
Unfortunately, I couldn't figure out how to render the Chart-control without adding a reference to the Page-object to it, and HtmlHelper in itself doesn't have a reference to it, so I had to add Page as a constructor parameter.
Html.Chart<datatype>(this.Page, Model.Data)
[If someone has a workaround for this, send me a mail.]
-==- HtmlHelper extension -==-
You can create extensions simply by:
public static FluentChart<T> Chart<T>(this HtmlHelper html, Page page, IEnumerable<T> data) {
return new FluentChart<T>(page, data);
}
The main "fluent interface" is the FluentChart<T> class.
public class FluentChart<T> {
...
public FluentChart<T> Width(Unit width) { Chart.Width = width; return this; }
public FluentChart<T> Height(Unit height) { Chart.Height = height; return this; }
...
}As there are more then 30 chart types and I didn't have time to implement them all, so I decided that each chart type should be it's own extension to FluentChart. I don't know if this is a good idea, but for prototyping the idea it seemed to fit.
Creating the extension:
public static FluentChart<T> Pie<T>(this FluentChart<T> chart, string title, Func<T, object> yvalue,
Action<DataPoint, T> datapointConfiguration) {
// configure area and series type and layout
...
// and set datapoints
foreach (var value in chart.Data) {
// Set values
int ptIdx = chart.Chart.Series[0].Points.AddY(yvalue(value));
DataPoint pt = chart.Chart.Series[0].Points[ptIdx];
// configure datapoint
if (datapointConfiguration != null) datapointConfiguration(pt, value);
}
// continue fluent interface
return chart;
}Pie returns the FluentChart<T> object, so user can continue configuring the main chart properties or adding more charts.
This is not a true fluent interface, as constructing the chart type (pie, bar, ...) is not truly fluent and contains all the configuration in single method. Hopefully I'll have more time later to expand it.
-==- Using anonymous types -==-
For linq group by operations using dynamic datatype should work with .Net framework 4.
var data = (from d in db.Table
group d by d.Name into g
select new { Name = g.Key, Sum = g.Group.Sum(p => p.Value) }
Html.Chart<dynamic>(...)
.Pie(
s => s.Sum, // dynamic operation
...
);
-==- Configuring ASP.Net MVC for Charting -==-
Note. The configuration is for .Net framework 3.5. For 4.0, change all 3.5.0.0 strings to 4.0.0.0.
<!-- configure image handler --> <!-- For detailed configuration information, I suggest reading Delian Tchiparinov's post: http://blogs.msdn.com/deliant/archive/2008/12/02/managing-chart-generated-images-with-chart-image-handler.aspx --> <appSettings> ... <add key="ChartImageHandler" value="storage=memory;timeout=20;"/> </appSettings> <!-- reference the assembly --> <assemblies> ... <add assembly="System.Web.DataVisualization, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/> </assemblies> <!-- add Html-extension --> <namespaces> ... <add namespace="Singularity.Charting"/> </namespaces> <httpHandlers> ... <add path="ChartImg.axd" verb="GET,HEAD" type="System.Web.UI.DataVisualization.Charting.ChartHttpHandler, System.Web.DataVisualization, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" validate="false"/> </httpHandlers> <system.webServer> <handlers> ... <remove name="ChartImageHandler"/> <add name="ChartImageHandler" preCondition="integratedMode" verb="GET,HEAD" path="ChartImg.axd" type="System.Web.UI.DataVisualization.Charting.ChartHttpHandler, System.Web.DataVisualization, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/> </handlers> </system.webServer>
-==- Sample -==-
I'm using the data from Scott Hanselman's 2010 Survey Results; I constructed a DataContainer with Name and Value and used it as the base data for the charts.
The ASP.Net MVC website is just what you get when you create a new site, except that HomeControllers Index method returns the data used for charts. Chart usage samples can be found in /Views/Home/Index.aspx
Note that the prototype is very simple. Hopefully it'll serve as a base for real implementation.
Prototype source can be found in here.
I use similar way as FluentChart.RenderChart() but I don't set the Chart.Page property and it works fine.
ReplyDelete