System.Web.Helpers.Chart Custom Themes

September 18, 2012

Nerd

I am in the process of moving some reporting I built in 2004 using the WebChart Control for ASP.NET from CarlosAg.net in a WebForms  site that used .NET FW 2.0 to a .NET FW 4.5 MVC4 site.

As you can see from this Tweet, I was pretty excited to see Razor Chart Helpers built into MVC.It started off great – this documentation looks AWESOME…. then I tried to customize how they look. Oye.

Images like these make it look incredibly customizable:

Description: Picture showing the chart elements

Unless I missed something HUGE, they’re very difficult to style. In fact, I couldn’t even find a property that enabled me to modify where the visibility of the labels on my pie chart, so it looked clustered like this:

image

Don’t get me wrong; There *are* options other than the default you see above, but they’re blanket themes, compiled into the dll. To see what I mean, see “Styling a Chart.”

Here’s what I get from those, in “Blue, Green, Vanilla, Vanilla3d, Yellow” theme order:

Bluegreen VanillaVanilla3d

Yellow

I have NO IDEA how they got this example I was lured in with, but I’m pretty sure it was *not* done with one of the 6 available themes:

Description: Picture of the Pie chart type

So, what did I do about it? I searched for the open source & came up empty, so I decompiled the System.Web.Helpers dll & discovered this:

image

Manually-formatted, long, kludge strings nearly impossible to change… and where did they come from!!?!?

So after a little while longer being nosy in my decompiler, I found references sending me over to the http://msdn.microsoft.com/en-us/library/system.web.ui.datavisualization.charting.chart.aspx class.

Aha! That’s where those came from but isn’t there a better way to create custom themes than hacking through these classes with esoteric, error-prone xml strings? There had to be, so I set out to find one.

Since that class was only returning (constant) strings, I built a method that took in a theme enum name & built the strongly-typed objects out, then serializes it out to xml (escaped & all).

I used the first one as an example test to see if this hair-brained desire was even possible.

This is what I started with (I had already been trying to tweak it manually with little luck & much frustration):

        // Summary:
        //     A theme for 2D charting that features a visual container with a blue gradient,
        //     rounded edges, drop-shadowing, and high-contrast gridlines.
  public const string Blude3d =
@”<Chart BackColor=””#D3DFF0″” BackGradientStyle=””TopBottom”” BackSecondaryColor=””White”” BorderColor=””26, 59, 105″” BorderlineDashStyle=””Solid”” BorderWidth=””2″” Palette=””None”” PaletteCustomColors=””97,142,206; 209,98,96; 168,203,104; 142,116,178; 93,186,215; 255,155,83; 148,172,215; 217,148,147; 189,213,151; 173,158,196; 145,201,221; 255,180,138″”>
    <ChartAreas> <ChartArea Name=””Default”” _Template_=””All”” BackColor=””64, 165, 191, 228″” BackGradientStyle=””TopBottom”” BackSecondaryColor=””White”” BorderColor=””64, 64, 64, 64″” BorderDashStyle=””Solid”” ShadowColor=””Transparent””>
            <Area3DStyle LightStyle=””Simplistic”” Enable3D=””True”” Inclination=””5″” IsClustered=””True”” IsRightAngleAxes=””True”” Perspective=””5″” Rotation=””0″” WallWidth=””0″” />
        </ChartArea></ChartAreas><Legends> <Legend  _Template_=””All”” BackColor=””Transparent”” Font=””Trebuchet MS, 8.25pt, style=Bold”” IsTextAutoFit=””False”” />   </Legends>  <BorderSkin SkinStyle=””Emboss”” /> </Chart>”;

This is what I wrote to (re)create that WITH Intellisense this time = hooray Intellisense

public static string GetTheme(IntelliboothTheme intelliboothTheme)
{
    ChartArea ca = new System.Web.UI.DataVisualization.Charting.ChartArea("Default");
    var chart = new System.Web.UI.DataVisualization.Charting.Chart();
    chart.BackColor = Color.Azure;
    chart.BackGradientStyle = GradientStyle.TopBottom;
    chart.BackSecondaryColor = Color.White;
    chart.BorderColor = Color.FromArgb(26, 59, 105);
    chart.BorderlineDashStyle = ChartDashStyle.Solid;
    chart.BorderWidth = 2;
    chart.Palette = ChartColorPalette.None;
    chart.PaletteCustomColors = new Color[] { Color.Lime, Color.Red,
Color.Orange, Color.Yellow, Color.Green, Color.Blue, Color.Purple,
Color.Black };
    chart.ChartAreas.Add(new ChartArea("Default")
    {
        BackColor = Color.FromArgb(64, 165, 191, 228),
        BackGradientStyle = GradientStyle.TopBottom,
        BackSecondaryColor = Color.White,
        BorderColor = Color.FromArgb(64, 64, 64, 64),
        BorderDashStyle = ChartDashStyle.Solid,
        ShadowColor = Color.Transparent,
        Area3DStyle = new ChartArea3DStyle()
        {
            LightStyle = LightStyle.Simplistic,
            Enable3D = true,
            Inclination = 5,
            IsClustered = true,
            IsRightAngleAxes = true,
            Perspective = 5,
            Rotation = 0,
            WallWidth = 0
        }
    });
    chart.Legends.Add(new Legend("All")
        {
            BackColor = Color.Transparent,
            Font = new Font("Trebuchet MS", 8.25f, FontStyle.Bold,
GraphicsUnit.Point),
            IsTextAutoFit = false
        }
        );
    chart.BorderSkin.SkinStyle = BorderSkinStyle.Emboss;

    var cs = chart.Serializer;
    cs.IsTemplateMode = true;
    //cs.Content = SerializationContents.Appearance;
    cs.Format = SerializationFormat.Xml;
    var sb = new StringBuilder();
    using (XmlWriter xw = XmlWriter.Create(sb))
    {
        cs.Save(xw);
    }
    string theme = sb.ToString().Replace("<?xml version=\"1.0\"
encoding=\"utf-16\"?>", "");
    return theme
}

Yes, I’m sure there’s some serializer setting I can set to prevent my manual stripping hack there at the end, but it wasn’t worth it to me, on little sleep, tonight.

I was just THRILLED to find a strongly-typed way to populate those styles

p.s. In case you’re wondering how I called my theme

@model Dictionary<string, float>

@{
    var xArray = new List<string>();
    var yArray = new List<float>();
    foreach (KeyValuePair<string, float> kvp in ViewData.Model)
    {
        xArray.Add(kvp.Key);
        yArray.Add(kvp.Value);
    }
    var myChart = new Chart(width: 500, height: 280, theme: System.Web.Helpers.IBChartTheme.GetTheme(IBTheme.Default)) // IBChartTheme.Blue3d)
        .AddTitle(“Registrations by Country”)
        .AddSeries(
        chartType: “pie”,
            xValue: xArray.ToArray(),
            yValues: yArray.ToArray())
            .AddLegend(“Countries”)
        .Write(“png”);
}

Summary

I have much more to explore & tweak to make my charts look like their examples, rather than my strongly-typed test that proved it worked, but I feel much more confident I’ll be *able* to get these to a better visible state.:

rainbow

9 Comments on “System.Web.Helpers.Chart Custom Themes”

  1. Raif Says:

    Hi, just started reading your blog so i don’t really know who you are yet (but i can feel that this is going to bite me in the ass).
    Having read two posts, this one and the one about what a pain in the ass it is to upgrade EF or FW or both, i have to ask. Why are you so persistent in the face of clearly bad usability, if not design, when there are so many other better choices?
    I mean i applaud the effort and resourcefulness but it’s at least questionable whether one should even use these frameworks. Much less go through these coniptions to make them work.
    I’ll read more and see if i can figure the angle. You’re obviously smart so it should be fun.

    Reply

  2. Gene Says:

    Hi Cori,
    This is great stuff. I too ran into the can’t configure on the fly and then found your post. Couple of questions For the life of me I can not find the IntelliboothTheme class nor could I find IBChartTheme. I too like you am doing this late at night and way to tired. But, you have put me on the right track and that is great.

    Thank you,
    Gene

    Reply

  3. Pete McPhearson Says:

    I found this pretty useful to getting to the bottom of what’s going on with the Chart stuff in the MVC helper stuff.
    Although, I’m not sure what you gain from the serializing to xml part of the process – the only reason I can see for dealing with all the gnarly XML definition stuff is so that you can change the XML on the page to alter the graph display without needing to change code – once you hard coding the styling, why not just write the chart from the controller?

    public void ChartImage()
    {
    Chart chart = GetChartWithTheme(IBTheme.Default);

    // add some data

    Chart.Write;
    }

    And then in the page use:
    img alt=”Graph of Results” src=”@Url.Action(“ChartImage”, “Home”)”

    This way the view code is really clean and you can share the chart creating code between pages or projects.

    Reply

    • Pete McPhearson Says:

      Actually, I see why you need the serializing to xml – it’s the only way to get at those properties since the Helper doesn’t expose them and you can’t actually Write the old ASPX chart to the response stream. (It’s kinda cobbled together to make it technically work in MVC, but it’s still a Webforms control under the hood.

      I still quite like to use an image and an action src rather than pumping all that XML to the view – building up a chart object in the razor view just seems a bit clunky.

      Did you find any good alternative to this hacking about with the old forms controls?

      Reply

      • TruncatedCoDr Says:

        Yes I finally realized it was really just an incomplete implementation in Razor & moved to using the original webcharts dll to solve the problems I couldn’t with the razor wrapper.

        Reply

  4. Savage Says:

    Lifesaver – thanks! I simply can’t find a manual explaining all the styling options, but at least with this approach the API is more discoverable.

    Reply

  5. Jana Says:

    Hi,

    I know this blog is old, but I think the problem has been solved now. The proper documentation for the Chart can be found here (makes lots of things easier):

    https://msdn.microsoft.com/en-us/library/Gg309515.aspx (just scroll down and look for ‘presentation description’)
    https://msdn.microsoft.com/en-us/library/system.windows.forms.datavisualization.charting.chart_properties%28v=vs.110%29.aspx
    https://msdn.microsoft.com/en-us/library/system.windows.forms.datavisualization.charting.series_properties%28v=vs.110%29.aspx
    https://msdn.microsoft.com/en-us/library/system.windows.forms.datavisualization.charting.chartarea%28v=vs.110%29.aspx
    – More custom properties can be found on the Microsoft Pages.
    https://msdn.microsoft.com/en-us/library/system.drawing.color%28v=vs.110%29.aspx (Colors)

    Example:
    string myTheme =
    @”

    “;

    And then just call (as an example):
    Chart chart = new Chart(width: 500, height: 280, theme: myTheme)
    .AddLegend(…)
    .AddSeries(…).AddSeries(…).AddSeries(…);

    Reply

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: