asp.Net MVC 如何为图表中的每个柱设置颜色?

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

我在 MVC 应用程序中使用 System.Web.Helpers。

该应用程序创建了多个图表,经过多次尝试,我终于成功了。

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");
};

结果:

问题是我希望能够更改条形图上的颜色。 像这样:

我找不到任何关于如何执行此操作的最新文章。 我找到的那些向我展示了如何在图表上设置主题,但如何在每个条形图上设置特定颜色?

图表带有一组预定义的 5 个主题。如果您想要自定义颜色,可以创建自定义主题。基本上就是这样一个XML。

<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>

因此,首先,您可以创建一个 class,其中包含此字符串

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>";
}

并使用它。

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

现在您可以考虑将此 XML 结构保存在一个真实的 XML 文件中并从中读取并使用它。您需要编写 C# 代码来读取文件和 return 它的字符串版本。

您也可以考虑 javascript 图表库,例如 Chart.js or Highcharts,它们可以让您以更广泛的方式自定义图表。

遗憾的是,图表助手是 MVC 没有公开任何属性来轻松设置每个单独的列/字段。因此,您将 have to resort to using a nasty stringified theme 如下所示:

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

然后将所述主题应用到您现有的 Chart :

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

然而,这些主题似乎非常有限,并且可能缺乏必要的功能来定位单个栏(至少很容易)。如果您需要这种功能,您可能需要考虑寻找替代库或第三方组件,例如 HighCharts

从控制器向 MVC 视图添加条形图

给条形图不同的颜色

好吧,老实说,我试图在我的视图中使用颜色条获取图表。为此,我阅读了一些堆栈溢出 post 和其他 material。简而言之:没有好的答案,不完整或缺乏好的例子。这就是为什么我决定为自己拼凑一些东西,因为我需要它,并且作为副作用为社区提供一些有用的东西。我给你所有的代码,控制器代码和视图。如有疑问:jaap.zwart@microtouchconsult.com

如果您开始从事严肃的项目,请不要忘记将其包含在适当的 DevOps 流程中。我自己总是使用 Microsoft Azure DevOps,因为它很棒。这意味着,创建一个项目,一个具有足够内容和任务的功能和用户故事的看板。之后,进行管道和发布管理,然后就可以了。

我们开始吧。先上图:

Implement Charts in MVC with color bars

好的,让我们看看控制器的功能。我将向您展示两个小节和一个四小节的示例。向您展示安排的灵活性。该代码不是超级优雅,需要更多的 OO 黑客攻击。这取决于你自己。作为一个不优雅的例子,有时更容易理解。

这是条形图两列的控制器方法:

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) ;
    }

这里值得一提的是 httpContext,Session[] 变量,在其他地方填充。当然,在 Web 环境中使用静态有点愚蠢,尽管它会给您的用户带来退出行为。可能您已经说过,“嘿,我的图表声明在哪里?”我已将其置于 class 中的全球可访问级别。我希望你对此有意见,你应该。例如,它很好。这是:

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

另请注意,只有一种方法并将其传递给零可能也不是最优雅的方法。由你来用更好的方式照亮世界。所以,下一个有点傻:

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

好吧,让我喂你四个酒吧的控制器方法:

 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);
    }

再次,这些值填充了会话变量,现在 DrawColoredChart 方法有 6 个值,我只使用其中的 4 个,再次有一些意见。让我们看一下 DrawColoredChart 方法,看看它能为我们提供什么:

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");
    }

虽然这是一种比较容易理解的方法,顺便说一句,私有的,但有一些事情需要说明一下。首先,制作彩条的关键在于细节:请参阅此方法中的此调用:

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

它有三个参数,条的名称,值和我们想要给它的颜色。我把酒吧的名字留空了,因为我不需要它。它的作用是将该值放在图表中彩色条的顶部。我很快就会回到 AddChartLabelSmall。此调用的 series1 是通过该调用上方的向下部分进行的,并为您提供了一些可能性来根据您的目的进行调整:

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

此方法中还有更多部分可以调整外观,为您提供一些游乐场,让一切变得有趣和甜蜜。备注此方法的结尾:我们 return 一张图像,该图像将显示在此 MVC 应用程序的视图中。

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

是的是的是的,我听到你说:但是 ms 参数声明在哪里?记住。它是在我们之前调用此方法时声明的:

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

现在您可能想知道此方法中使用的 AddChartLabelSmall("", v1, "green")) 方法中的内容。一起来看看:

 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,
        };

    }

这是定义条形和颜色的实际位置,非常重要。特别注意此方法的 DataPoint 部分,它还调用另一个方法 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;
    }

最终确定了需要使用什么颜色来绘制条形图。好的,就是这样。我们需要做的最后一件事是查看视图。让我们看看这会给我们带来什么:

基本上它会向您展示的是 html 创建 table 并将图表图像推入 table 的 td,我应该说这很简单。

<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 />

请注意,因为我们 return 控制器方法中的图像,我们不需要带有模型的视图或其他东西。只是调用图像中的 Controller Action 方法并为图像本身添加 CSS 'cover' class:

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

有点过时了,因为我也把一些东西放在了 td 本身的样式中。

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

这是方法的调用堆栈:

Call stack of the methods

最后要提及的重要事项是我们使用了哪个图表,因为您在助手中有一个图表,还有另一个图表。我用了另一个,这里是使用:

using System.Web.UI.DataVisualization.Charting;

好的,我并不是说这都是初学者的东西,但现在应该很容易实现了。

给他们看点东西!

编码愉快。

这些是生成的 chrts, 酒吧.

The resulting bar charts

附加信息


在某些方法中,调用了共享模型 class。我忘了将那个添加到 post。就是这个:

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

好吧,让我们看看这个class里面。我把它放在一个名为 Models 的文件夹中,这是一个好习惯。

 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; }
}

}

虽然我已经添加了方法的调用堆栈和一些 activity 图表,但让我添加更多图表,让您的生活更上一层楼。

Class 图:

Class diagram of bar chart solution

为了使我之前添加的调用堆栈更专业一点,我将添加一个序列图:

Sequence diagram of the objects

关于添加带有彩色条形的条形图的 post 总共结束了。如前所述,创建良好的用户故事和任务是一个好习惯。通常情况下,图表和附加信息将添加到用户故事中,其中应该添加连接不同功能的更高级别的体系结构模型和文档,并在将更多用户故事组合在一起的功能中进行描述。往下你会找到一个解释这个原理的好页面:

Explaining Epics, Features and User Stories

这里有一些关于绘制功能图的内容

UML diagraming your solutions

祝您编码和绘图愉快!