ThunD3eR
ThunD3eR

Reputation: 3456

asp.Net MVC How to set colors to each bar in chart?

I am using System.Web.Helpers in a MVC application.

The application creates multiple charts and after many attempts I finaly made it work.

foreach (var m in model[0].HistoryValues)
{

    var chart = new Chart(width: 600, height: 400)
    .AddTitle(m.LastUpdateSTR)
    .AddSeries(
        name: m.LastUpdateSTR,
        xValue: new[] { "Server", "Db", "Tickets" },
        yValues: new[] { m.ServerPerformance, m.Databaseperformance, m.SoldTicketsLastThirtyMin == 0 ? 10 : m.SoldTicketsLastThirtyMin }        
    );

    m.Bytes = chart.GetBytes("jpeg");
};

Result:

enter image description here

The thing is that I want to be able to change the color on the bars. like so:

enter image description here

I have not been able to find any recent articles on how to do this. The ones I have found show me how to set a theme on the chart but how do I set a specific color on each bar?

Upvotes: 2

Views: 2788

Answers (3)

Jaap Zwart
Jaap Zwart

Reputation: 1

Adding Bar Charts to MVC Views from Controller

Give the bars different colors

Well, to be honest, I was trying to get Chart in my views with color bars. For that I have read some stack overflow posts and other material. In short: no good answers, incomplete or lacked good examples. That's why I decided to hack something together for myself, cause I needed it, and as a side effect offer the community something that works. I give you all the code, the controller code and the view(s). If you have questions: [email protected]

If you start on serious projects, don't forget to include it in proper DevOps processes. I myself always use Microsoft Azure DevOps, cause that rocks. That means, create a project, a board with Features and User Stories with enough content and tasks. After that, do your pipelines and release management and off you go.

Here we go. First the diagram:

Implement Charts in MVC with color bars

Ok, lets see the controller functionality. I'll show you an example for two bars and one for four bars. To show you how flexible that can be arranged. The code is not super elegant and needs some more OO hacking. That's up to yourself. As an example not being elegant makes it sometimes easier to follow.

Here is the Controller method for the bar chart two columns:

public ActionResult DrawChartTwoColors()
    {
        double val1 = Convert.ToDouble(HttpContext.Session["perGreen"])
              + Convert.ToDouble(HttpContext.Session["perLightgreen"]);
        double val2 = Convert.ToDouble(HttpContext.Session["perOrange"])
            + Convert.ToDouble(HttpContext.Session["perRed"]);
        chartModel.val1 = Math.Round(val1, 2);
        chartModel.val2 = Math.Round(val2, 2);
        chartModel.maxVal = 50;

        var ms = new MemoryStream();
        return DrawColoredChart(ms, false,
            val1, val2, 0, 0, 0, 0) ;
    }

Interesting to mention here are the httpContext,Session[] vars, being filled somewhere else. Of course in a web environment using statics is a bit silly, although it gives your users exiting behavior. Probably you've said already, "hey, where is my chart declaration?" I've put that one on a globally accessible level in the class. I hope you have an opinion about that, you should. For example purposes it is fine. Here it is:

public Models.ChartModel chartModel = new Models.ChartModel();

Remark also that having one method and passing it zeros is probably also not the most elegant one. Up to you to enlighten the world with better. So, down one is a bit silly:

return DrawColoredChart(ms, false,
            val1, val2, 0, 0, 0, 0) ;

Okay, let me feed you the Controller method for four bars:

 public ActionResult DrawChartFourColors()
    {
        var val1 = Convert.ToDouble(HttpContext.Session["perGreen"]);
        var val2 = Convert.ToDouble(HttpContext.Session["perLightgreen"]);
        var val3 = Convert.ToDouble(HttpContext.Session["perRed"]);
        var val4 = Convert.ToDouble(HttpContext.Session["perOrange"]);
        var val5 = Convert.ToDouble(HttpContext.Session["perGreen"])
               + Convert.ToDouble(HttpContext.Session["perLightgreen"]);
        var val6 = Convert.ToDouble(HttpContext.Session["perOrange"])
            + Convert.ToDouble(HttpContext.Session["perRed"]);
        chartModel.val1 = Math.Round(val1, 2);
        chartModel.val2 = Math.Round(val2, 2);
        chartModel.val3 = Math.Round(val4, 2);
        chartModel.val4 = Math.Round(val3, 2);
        chartModel.val5 = Math.Round(val5, 2);
        chartModel.val6 = Math.Round(val6, 2);

        chartModel.maxVal = 50;
        var ms = new MemoryStream();
        return DrawColoredChart(ms, true,
            val1, val2, val3, val4, val5, val6);
    }

Again, the values are filled with Session vars and now the DrawColoredChart method has 6 values of which I only use 4, again something to have an opinion about. Let us have a look into that DrawColoredChart method and see what it has to offer us:

private ActionResult DrawColoredChart(MemoryStream ms, bool large,
        double v1, double v2, double v3, double v4, double v5, double v6)
    {
        try
        {
           

            Chart chart = new Chart();
            chart.BackColor = Color.White;
            chart.BorderlineWidth = 10;
            chart.BorderlineDashStyle = ChartDashStyle.Solid;


            chart.BorderlineColor = Color.LightBlue;

            chart.Width = Unit.Pixel(250);
            chart.Height = Unit.Pixel(150);

            Series series1 = new Series("Series1");
            series1.ChartArea = "ca1";
            series1.ChartType = SeriesChartType.Bar;
            series1.Font = new System.Drawing.Font("Verdana", 10f, FontStyle.Bold);

            var viewModelChart = new Models.ChartModel();

            viewModelChart.maxVal = 0;

            if (large)
            {
                series1.Points.Add(AddChartLabelSmall("", v1, "green"));
                series1.Points.Add(AddChartLabelSmall("", v2, "lightgreen"));
                series1.Points.Add(AddChartLabelSmall("", v3, "orange"));
                series1.Points.Add(AddChartLabelSmall("", v4, "red"));
            }
            else
            {
                series1.Points.Add(AddChartLabelSmall("", v1, "green"));
                series1.Points.Add(AddChartLabelSmall("", v2, "red"));
            }

            chart.Series.Add(series1);

            ChartArea ca1 = new ChartArea("ca1");
            ca1.BackColor = Color.Transparent;
            chart.ChartAreas.Add(ca1);
            chart.ChartAreas[0].AxisX.LineDashStyle = ChartDashStyle.Dash;
            chart.ChartAreas[0].AxisX2.LineDashStyle = ChartDashStyle.NotSet;
            chart.ChartAreas[0].Area3DStyle.Enable3D = true;
            chart.SaveImage(ms, ChartImageFormat.Png);
            ms.Seek(0, SeekOrigin.Begin);
        }
        catch { }
        return new FileStreamResult(ms, "image/png");
    }

Although this is rather an easy to understand method, private by the way, there are some things to remark about this one. First of all, the meat of making colored bars is in the details: see this call inside this method:

series1.Points.Add(AddChartLabelSmall("", v1, "green"));

It has three params, name of the bar, the value and which color we wanna give it. I've left the name of the bar empty cause I did not needed it. What it does it put that value on top of the colored bar in your chart. I will come back to the AddChartLabelSmall soon. The series1 of this call is made through down part which is just above that call and gives you some possibilities to adjust whatever you want for your purposes:

Series series1 = new Series("Series1");
            series1.ChartArea = "ca1";
            series1.ChartType = SeriesChartType.Bar;
            series1.Font = new System.Drawing.Font("Verdana", 10f, FontStyle.Bold);

There are some more parts to adjust the look and feel in this method which gives you some playground to make it all fun and sweet. Remark also the end of this method: we return an image, the image that will be shown in the view of this MVC app.

return new FileStreamResult(ms, "image/png");

Yes yes yes, I hear you say: but where is that ms parameter declared? Remember. it was declared when we called this method earlier:

var ms = new MemoryStream();
    return DrawColoredChart(ms, false,
        val1, val2, 0, 0, 0, 0) ;

Probably now you wanna know what's in that AddChartLabelSmall("", v1, "green")) method being used in this method. Let's take a look:

 private DataPoint AddChartLabelSmall(string name, double value, string colorS)
    {
        Color colText = Color.Black;
        Color colGet = Color.Blue;

        int colorBar = 0;
        if (colorS.Contains("green"))
        {
            colGet = Color.Green;
            colorBar = 1;
        }
        if (colorS.Contains("lightgreen"))
        {
            colGet = Color.LightGreen;
            colorBar = 2;
        }
        if (colorS.Contains("orange"))
        {
            colGet = Color.Orange;
            colorBar = 3;
        }
        if (colorS.Contains("red"))
        {
            colGet = Color.Red;
            colorBar = 4;
        }
        if (colorS.Contains("pergreen"))
        {
            colGet = Color.Green;
            colorBar = 5;
        }
        if (colorS.Contains("perred"))
        {
            colGet = Color.Red;
            colorBar = 6;
        }



        if (colGet == Color.Green || colGet == Color.Red)
            colText = Color.Yellow;
        else
            colText = Color.Black;

        return new DataPoint
        {
            YValues = new double[] { value },
            Color = GetColorBar(colorBar),
            Font = new System.Drawing.Font("Verdana", 6f, FontStyle.Bold),
            Label = name,
            BorderColor = GetColorBar(colorBar),
            LabelBackColor = GetColorBar(colorBar),
            LabelForeColor = colText,
        };

    }

It's the actual place where the bars and colors are defined, so pretty important. Especially give attention to the DataPoint part of this method, which also calls another method being the GetColorBar():

 public Color GetColorBar(int valueBar)
    {
        if (valueBar == 1)
        {
            return Color.Green;
        }
        if (valueBar == 2)
        {
            return Color.LightGreen;
        }
        if (valueBar == 3)
        {
            return Color.Orange;
        }
        if (valueBar == 4)
        {
            return Color.Red;
        }
        if (valueBar == 5)
        {
            return Color.Green;
        }
        if (valueBar == 6)
        {
            return Color.Red;
        }

        return Color.Orange;
    }

It finally determines what colors needs to be used to draw the bar. Okay, that's about it. The last thing we need to do is take a look at is the View. Lets see what that'll bring us:

Basically what it will show you is the html creating a table and pushing the chart images into the td's of that table, easy enough I should say.

<table id="customers" style="border: 1px solid grey; background-color: white; margin-right: 20px;">
    <tr>
        <th>Verzamel kleuren Chart</th>
        <th>Alle kleuren Chart</th>
    </tr>
    <tr>
        <td style="border: 1px solid grey; background-color: white; margin-right: 20px;align-content:stretch" height="200">
            <center>
                <img class="cover" src="@Url.Action("DrawChartTwoColors", "Home")"/>
            </center>
        </td>
        <td style="border: 1px solid grey; background-color: white; margin-right: 20px;align-content:stretch" height="200">
            <center>
                <img class="cover" src="@Url.Action("DrawChartFourColors", "Home")"/>
            </center>
            
        </td>
    </tr>
</table><br />

Remark that because we return images in the Controller methods, we don't need a view with a model or something. Just the call to the Controller Action methods inside an image and to spice it up a CSS 'cover' class for the image itself:

.cover {
    width: 100%;
    height: 100%;
}

A bit double dated cause I also put something in the style of the td itself.

<td style="border: 1px solid grey; background-color: white; margin-right: 20px;align-content:stretch" height="200">

Here is the call stack of the methods:

Call stack of the methods

A final important thing to mention is which Chart we used, cause you have one in the Helpers and another one. I used the other one, here is the using:

using System.Web.UI.DataVisualization.Charting;

Okay, I'm not saying it is all beginners stuff, but this all should now be easy to implement.

Show'em something!

Happy coding.

These are the resulting charts, bars.

The resulting bar charts

Additional information


In some of the methods a shared Model class is called. I forgot to add that one to this post. It's this one:

public Models.ChartModel chartModel = new Models.ChartModel();

okay, lets have a look inside this class. I've put it in a folder named Models, good habit to do that.

 public class ChartModel
{
    public double maxVal { get; set; }
    public double val1 { get; set; }
    public double val2 { get; set; }
    public double val3 { get; set; }
    public double val4 { get; set; }
    public double val5 { get; set; }
    public double val6 { get; set; }
}

}

Although I've added a call stack for the methods and some activity diagrams, let me add some more diagrams to make your life even more upsytopsy.

Class diagram:

Class diagram of bar chart solution

To make the call stack I've added earlier a bit more pro, I will add a sequence diagram:

Sequence diagram of the objects

This in total concludes the post around adding a bar chart with colored bars. Its good habit to create, as said before, good user stories and tasks. Normally the diagrams and additional information would be added to the user stories where higher level architectural models and documents connecting different functionalities should be added and described in a feature combining more user stories together in an overall piece of functionality. Down you'll find a good page explaining this principle:

Explaining Epics, Features and User Stories

Here's something on diagramming your functionality

UML diagraming your solutions

Happy coding and diagramming!

Upvotes: 0

Rion Williams
Rion Williams

Reputation: 76597

The Chart Helper is MVC sadly doesn't expose any properties for easily setting each of your individual columns / fields. As a result, you'll have to resort to using a nasty stringified theme as demonstrated below :

var theme = @"<Chart BackColor="Transparent">
                  <ChartAreas>
                       <ChartArea Name="Default" BackColor=""Transparent"></ChartArea>
                  </ChartAreas>
              </Chart>";

And then applying said theme to your existing Chart :

var chart = new Chart(width: 600, height: 400, theme: theme)

These themes however seem to be quite limited and may lack the necessary functionality to be able to target individual bars (at least easily). You may want to consider looking into an alternative library or third-party component like HighCharts if you need this kind of functionality.

Upvotes: 2

Shyju
Shyju

Reputation: 218892

Chart comes with a predefined set of 5 themes. If you want custom colors, you can create a custom theme. It is basically an XML like this.

<Chart BackColor="#D3DFF0" BackGradientStyle="TopBottom" BackSecondaryColor="White" BorderColor="26, 59, 105" BorderlineDashStyle="Solid" BorderWidth="2" Palette="BrightPastel">
  <ChartAreas>
    <ChartArea Name="Default" _Template_="All" BackColor="64, 165, 191, 228" BackGradientStyle="TopBottom" BackSecondaryColor="White" BorderColor="64, 64, 64, 64" BorderDashStyle="Solid" ShadowColor="Transparent" />
  </ChartAreas>
  <Legends>
  <Legend _Template_="All" BackColor="Transparent" Font="Trebuchet MS, 8.25pt, style=Bold" IsTextAutoFit="False" /><BorderSkin SkinStyle="Emboss" />
</Chart>

So to get started, you can create a class which has this string

public static class MyChartTheme
{
    public const string MyCustom = "<Chart BackColor=\"White\" BackGradientStyle=\"TopBottom\" BackSecondaryColor=\"White\" BorderColor=\"26, 59, 105\" BorderlineDashStyle=\"Solid\" BorderWidth=\"2\" Palette=\"BrightPastel\">\r\n    <ChartAreas>\r\n        <ChartArea Name=\"Default\" _Template_=\"All\" BackColor=\"64, 165, 191, 228\" BackGradientStyle=\"TopBottom\" BackSecondaryColor=\"White\" BorderColor=\"64, 64, 64, 64\" BorderDashStyle=\"Solid\" ShadowColor=\"Transparent\" /> \r\n    </ChartAreas>\r\n    <Legends>\r\n        <Legend _Template_=\"All\" BackColor=\"Transparent\" Font=\"Trebuchet MS, 8.25pt, style=Bold\" IsTextAutoFit=\"False\" /> \r\n    </Legends>\r\n    <BorderSkin SkinStyle=\"Emboss\" /> \r\n  </Chart>";
}

and use it.

var chart= new Chart(width: 600, height: 400, theme: MyChartTheme.MyCustom)

Now you may consider keeping this XML structure in a real XML file and read from that and use that. You need to write C# code to read the file and return the string version of it.

You may also consider javascript charting libraries like Chart.js or Highcharts and they will let you customize the charts in a broader way.

Upvotes: 1

Related Questions