An introduction to custom SVG charting using MVC and Razor
To find out more about Concentra's bespoke Analytics Services and Solutions see Services.
However, the combination of complex charts and slow client machines means that using front end code to render graphics can be slow. To combat performance issues, we can render SVG documents through the back end using features of ASP.NET MVC.
This blog post will cover a basic implementation of SVG rendering using MVC and the Razor view engine. It will also cover the structuring of an SVG document for charting and non-SVG compatability.
Using MVC, we can generate our data objects from the database (or any other data source) transform them into our view model and provide the values to our SVG view.
Displaying SVG in your MVC application is as simple as embedding the SVG tags directly in your razor views.
However, you may find embedding your SVG content along with your regular HTML elements will result in very messy views. This is where partial views come in handy. Using partial views, we can separate our SVG content into their own views and even split off common SVG blocks into smaller partial views.
Below shows an example of a partial view being used to render a SVG circle element.
Figure 1. Partial View using ASP.NET MVC
Further extending this idea, we can create our own custom HtmlHelpers to return SVG markup based on defined methods and parameters:
We can then use the helper in our view by calling:
@Html.SvgHelper.SvgCircle(20, 20, 10)
Which produces our SVG circle element:
<circle cx="20" cy="20" r="10">
The resulting effect of using these extensions is a cleaner, clearer SVG Razor view with reusable elements.
Structuring the SVG document
Now that we have our helpers and partial views, it's time to start structuring our SVG. An important note about SVG is that it uses a painters model (i.e. elements which appear later in the document are displayed on top of previous ones). This means that you will need to consider the order in which your elements appear in the document.
After declaring our document type and root SVG (<svg>) node, we can start drawing our elements. Letâ€™s start by splitting up the components which make up our basic chart:
- Chart Axes
- Series (lines)
SVG provides a grouping element (<g>) which we can use to group sub elements together to form our chart components. Using groups also allow us to apply transformation and effects on multiple elements nested within the group.
In the case of our chart, we may want to apply a grouping to a series along with its points so that any effects are applied to both elements. The example below shows a line and a point grouped so that we can apply a CSS hover effect on the line when the users mouse overs the group:
It is important to note that when an element is added to a group, the group will expand and "wrap" all child elements. This can have unwanted effects when you style your graphic with hover events, etc.
We can now put together our SVG chart, keeping in mind the painters model, so that groups which appear at the back are rendered first and groups we want to see on top are rendered last.
First we render the background grid lines, then the relevant labels. Next, we draw the line groups and styles (e.g. shadow elements behind the line elements). Finally, we render the points to sit on the lines. Below shows a graphical view of the structure of our SVG document:
Using the above structure, we can see the progression of layers of our chart structure.
Because SVG is not compatible with IE8 and lower, we need to create a non-SVG view for clients running older browsers. The typical way this is handled in SVG libraries is too render the VML (Vector Markup Language) equivalent of the chart. This means you essentially have to code two versions of your graphic, one in SVG and one in VML.
Another solution is to use a flash rendering engine such as SVGWeb. This tool converts your SVG object into flash, retaining most of the functionality. However, this is making the assumption that all users have flash installed. It also means that we are required to load an additional plugin, slowing down the total render time of the graphic.
The solution we chose was to render the SVG graphic as an image using backend code. This allows us to keep one version of the SVG and render a near identical image version to non-SVG compatible browsers. Using the same pixel coordinates of the SVG, we can then create an image map to layer over the image to provide functionality such as tooltips and hover styling.
Although we lose some interactivity (such as CSS hover styles and animation) we retain the same SVG object without having to rewrite or translate our SVG to VML. Additional interactivity can be achieved through external scripts using the image map and our original SVG coordinates.
*The header image used in this blog is credited to Highcharts