0 Comments

Almost web developers remembered that ASP.NET support chart controls since ASP.NET 3.5 as separate package that you can download it easily, after awhile they become built-in in ASP.NET 4, for more details you can refer to ScottGu's blog post here.

In this blog post I'll explain how can we develop some of the common charting controls that our applications & websites many need with the help of the tag helpers and morris.js.

Morris.js is an open source javascript library that powers the graphs. It has a very simple APIs for drawing line, bar, area and donut charts.

1. Line Charts

2. Area Charts

3. Bar Charts

4. Donut Charts


For more information about morris.js, please visit this link.

Now let us dig into chart controls and how I Create them. First of all we need to define the chart types using the enumeration

public enum ChartType
{
Area,
Bar,
Donut,
Line
}

After that we 'll create ChartTagHelper that calling morris.js APIs behind the scene.

public class ChartTagHelper: TagHelper
{
private IList<PropertyInfo> Properties => Source.First().GetType().GetProperties().ToList();

private IList<string> PropertyNames => Properties.Select(p => p.Name).ToList();

[HtmlAttributeName("id")]
public string Id { get; set; }

[HtmlAttributeName("type")]
public ChartType Type { get; set; }

[HtmlAttributeName("labels")]
public string Labels { get; set; }

[HtmlAttributeName("source")]
public IEnumerable<object> Source { get; set; }

public override void Process(TagHelperContext context, TagHelperOutput output)
{
output.Attributes.SetAttribute("id", Id);
output.TagName = "div";

if (Source == null)
{
return;
}

if (Type == ChartType.Donut)
{
output.PostElement.AppendHtml($@"
<script>
Morris.{Type}({{
element: '{Id}',
data: [
{ConstructDonutChartData()}
]
}});
</script>");
}
else
{
var xKey = PropertyNames.First();
var yKeys = PropertyNames.Skip(1).ToList();

output.PostElement.AppendHtml($@"
<script>
Morris.{Type}({{
element: '{Id}',
data: [
{ConstructChartData()}
],
xkey: '{xKey}',
ykeys: [{String.Join(",",yKeys.Select(p=> "'" + p + "'"))}],
labels: [{String.Join(",",Labels.Split(',').Select(l => "'" + l + "'"))}]
}});
</script>");
}
}

private string ConstructDonutChartData()
{
var labelProperty = Source.First().GetType().GetProperty("label");
var valueProperty = Source.First().GetType().GetProperty("value");

return String.Join(",", Source.Select(s =>
$"{{label: '{labelProperty.GetValue(s)}', value: {valueProperty.GetValue(s)}}}"));
}

private string ConstructChartData()
{
var xKey = PropertyNames.First();
var xProperty = Properties.First();
var yProperties = Properties.Skip(1);

return String.Join(",",
Source.Select(s => $"{{{xKey}:'{xProperty.GetValue(s)}', " + String.Join(",", yProperties.Select(p => p.Name + ":" + p.GetValue(s))) + "}"
));
}
}

The code above is simply constructs the script that morris.js needs to call its APIs, the code using reflection to get the array properties and their values, I know it needs a lot of improvement, which i 'll try to do if I get a time :)

Last thing we can use our tag helper like this:

@{
var contribution = new []
{
new { year = "2016-1", i = 100, pr = 34, c = 12},
new { year = "2016-2", i = 75, pr = 11, c = 3},
new { year = "2016-3", i = 50, pr = 99, c = 7},
new { year = "2016-4", i = 75, pr = 56, c = 1},
new { year = "2016-5", i = 24, pr = 0, c = 54},
new { year = "2016-6", i = 75, pr = 31, c = 3},
new { year = "2016-7", i = 100, pr = 77, c = 44}
};
}

<chart id="bar-example" type="Bar" labels="Issues,Pull Requests,Comments" source="@contribution"></chart>

The same thing for other chart types except you need to change the ChartType property.

You can download the source code for this blog post from my ChartControls repository on GitHub.