Friday, March 21, 2014

Графики в ASP.NET MVC

В этой статье мы рассмотрим способы построения графиков на ASP.NET MVC 4 (Razor). Раньше я считал, что это не должно составлять особых проблем, так как существует огромное количество контролов для построения графиков, а некоторые идут в составе .NET Framework 4.0. Построение графиков на ASP.NET MVC – процесс несложный, но интуитивно понятным его не назовешь. Рассмотрим процесс построения графиков на примере. Для построения примеров я использовал Visual Studio 2012 и ASP.NET MVC4. Для этого при создании нового проекта выберем создание проекта ASP.NET MVC 4 Web Application, чтобы создать наш проект со всеми привязками.
Следующим шагом выберем, какой проект мы хотим создать.
Поскольку мы создаем простой сайт, выбираем проект Internet Application и выберем движок представления ASP.NET Razor. Я выбрал Razor, потому что для меня он удобнее движка, используемого ранее ASPX. Если вы желаете более детально познакомиться с преимуществами Razor, рекомендую посмотреть статью на habrahabr: "Razor – новый движок представлений в ASP.NET". После того создания нашего веб-сайта переходим в папку Controllers и открываем класс HomeController.cs. При желании можно запустить ваш проект и посмотреть результат, который получился, перед тем как править код.
Для создания графиков нам понадобится добавить ссылку в наш проект на сборку System.Web.DataVisualization.
За основу примера взят код с сайта codeproject.com  ASP.NET MVC Chart Control.
Посмотрим, как выглядит пример использования кода.
public FileContentResult GetChart()
{
       var dates = new List<Tuple<int, string>>(
             new[]
                    {
                           new Tuple<int, string> (65, "January"),
                           new Tuple<int, string> (69, "February"),
                           new Tuple<int, string> (90, "March"),
                           new Tuple<int, string> (81, "April"),
                           new Tuple<int, string> (81, "May"),
                           new Tuple<int, string> (55, "June"),
                           new Tuple<int, string> (40, "July")
                    }
             );

       var chart = new Chart();
       chart.Width = 700;
       chart.Height = 300;
       chart.BackColor = Color.FromArgb(211, 223, 240);
       chart.BorderlineDashStyle = ChartDashStyle.Solid;
       chart.BackSecondaryColor = Color.White;
       chart.BackGradientStyle = GradientStyle.TopBottom;
       chart.BorderlineWidth = 1;
       chart.Palette = ChartColorPalette.BrightPastel;
       chart.BorderlineColor = Color.FromArgb(26, 59, 105);
       chart.RenderType = RenderType.BinaryStreaming;
       chart.BorderSkin.SkinStyle = BorderSkinStyle.Emboss;
       chart.AntiAliasing = AntiAliasingStyles.All;
       chart.TextAntiAliasingQuality = TextAntiAliasingQuality.Normal;
       chart.Titles.Add(CreateTitle());
       chart.Legends.Add(CreateLegend());
       chart.Series.Add(CreateSeries(dates, SeriesChartType.Line, Color.Red));
       chart.ChartAreas.Add(CreateChartArea());

       var ms = new MemoryStream();
       chart.SaveImage(ms);
       return File(ms.GetBuffer(), @"image/png");
}
Создание заголовка выглядит следующим образом:
[NonAction]
public Title CreateTitle()
{
       Title title = new Title();
       title.Text = "Result Chart";
       title.ShadowColor = Color.FromArgb(32, 0, 0, 0);
       title.Font = new Font("Trebuchet MS", 14F, FontStyle.Bold);
       title.ShadowOffset = 3;
       title.ForeColor = Color.FromArgb(26, 59, 105);

       return title;
}
Для создания серии воспользуемся методом CreateSeries.
[NonAction]
public Series CreateSeries(IList<Tuple<int, string>> results,
       SeriesChartType chartType,
       Color color)
{
       var seriesDetail = new Series();
       seriesDetail.Name = "Result Chart";
       seriesDetail.IsValueShownAsLabel = false;
       seriesDetail.Color = color;
       seriesDetail.ChartType = chartType;
       seriesDetail.BorderWidth = 2;
       seriesDetail["DrawingStyle"] = "Cylinder";
       seriesDetail["PieDrawingStyle"] = "SoftEdge";
       DataPoint point;

       foreach (var result in results)
       {
             point = new DataPoint();
             point.AxisLabel =result.Item2;
             point.YValues = new double[] { result.Item1 };
             seriesDetail.Points.Add(point);
       }
       seriesDetail.ChartArea = "Result Chart";

       return seriesDetail;
}
Создание легенды выглядит следующим образом:
[NonAction]
public Legend CreateLegend()
{
       var legend = new Legend();
       legend.Name = "Result Chart";
       legend.Docking = Docking.Bottom;
       legend.Alignment = StringAlignment.Center;
       legend.BackColor = Color.Transparent;
       legend.Font = new Font(new FontFamily("Trebuchet MS"), 9);
       legend.LegendStyle = LegendStyle.Row;

       return legend;
}
Последним штрихом создадим область, в которой данный график будет отображен.
[NonAction]
public ChartArea CreateChartArea()
{
       var chartArea = new ChartArea();
       chartArea.Name = "Result Chart";
       chartArea.BackColor = Color.Transparent;
       chartArea.AxisX.IsLabelAutoFit = false;
       chartArea.AxisY.IsLabelAutoFit = false;
       chartArea.AxisX.LabelStyle.Font = new Font("Verdana,Arial,Helvetica,sans-serif", 8F, FontStyle.Regular);
       chartArea.AxisY.LabelStyle.Font = new Font("Verdana,Arial,Helvetica,sans-serif", 8F, FontStyle.Regular);
       chartArea.AxisY.LineColor = Color.FromArgb(64, 64, 64, 64);
       chartArea.AxisX.LineColor = Color.FromArgb(64, 64, 64, 64);
       chartArea.AxisY.MajorGrid.LineColor = Color.FromArgb(64, 64, 64, 64);
       chartArea.AxisX.MajorGrid.LineColor = Color.FromArgb(64, 64, 64, 64);
       chartArea.AxisX.Interval = 1;
       return chartArea;
}
После проделанных действий нам необходимо перейти в форму View/Home и открыть нашу форму Index.cshtml.
С данного файла удаляем почти весь код. Оставим только строки:
@{
    ViewBag.Title = "Home Page";
}

<div>
    <img src="@Url.Action("GetChart")" />
</div>
После запуска проекта сможем увидеть результат в браузере.
Есть два способа вызвать какой-то метод у контроллера @Url.Action и  @Html.Action. Но между ними есть отличие. Метод @Html.Action генерирует код <a href=”..”><a> , а метод @Url.Action возвращает просто url. Пример:
@Html.ActionLink("link text", "someaction", "somecontroller", new { id = "123" }, null)
Сгенерирует код:
<a href="/somecontroller/someaction/123">link text</a>
А метод
@Url.Action("someaction", "somecontroller", new { id = "123" })
создаст код следующего вида:
/somecontroller/someaction/123
Думаю, отличие, а также причина использования в примере метода @Url.Action понятно.
Но если вам нужно построить простой график, то в ASP.NET MVC есть более простой способ создать график. Для этого нужно воспользоваться пространством имен System.Web.Helpers. Переходим снова в наш класс HomeController.cs и добавляем алиас на данное пространство имен.
using SimpleChart = System.Web.Helpers;
Это необходимо для того чтобы не удалять предыдущий написанный код, так как контрол Chart есть как в одном пространстве имен, так и в другом. Добавление алиаса позволяет решить эту проблему. Затем добавим метод CreateChart.
public ActionResult CreateChart()
{
       var chart = new SimpleChart.Chart(width: 700, height: 300)
             .AddTitle("График посещений")
             .AddSeries(
                    name: "Моя программа",
                    legend: "Моя программа",
                    chartType: "Line",
                    xValue: new[] { "Peter", "Andrew", "Julie", "Mary", "Dave" },
                    yValues: new[] { "2", "6", "4", "5", "3" })
             .Write();

       return null;
}
После необходимо перейти в наше представление (View) Index.cshtml и изменить метод в @Url.Action на следующий:
<div>
    <img src="@Url.Action("CreateChart")" />
</div>
После этого можно запустить наше приложение и посмотреть на результат.
Внимательный читатель заметит, что в графике не достает легенды, подписи, что означает конкретная линия, хотя мы ее и указали в строке
public ActionResult CreateChart()
{
       var chart = new SimpleChart.Chart(width: 700, height: 300)
             .AddTitle("График посещений")
             .AddSeries(
                    name: "Моя программа",
                    legend: "Моя программа", //Легенда не работает
                    chartType: "Line",
                    xValue: new[] { "Peter", "Andrew", "Julie", "Mary", "Dave" },
                    yValues: new[] { "2", "6", "4", "5", "3" })
             .Write();

       return null;
}
Исправить это несложно:
public ActionResult CreateChart()
{
       var chart = new SimpleChart.Chart(width: 700, height: 300)
             .AddTitle("График посещений")
             .AddSeries(
                    name: "Моя программа",
                    chartType: "Line",
                    xValue: new[] { "Peter", "Andrew", "Julie", "Mary", "Dave" },
                    yValues: new[] { "2", "6", "4", "5", "3" })
             .AddLegend()
             .Write();

       return null;
}
Если мы забудем удалить переменную legend с метода AddSeries, то получим ошибку следующего содержания:
"Series 'Моя программа' uses non-existing legend name 'Моя программа'. Set Legend property value to Default".
Либо нужно удалять параметры name и legend с метода AddSeries и инициализировать их в методе AddLegend. Это не все проблемы с этим контролом. Отдельно хотелось бы сказать об изменении темы для Chart. По умолчанию мы можем создать только 5 тем: Blue, Green, Vanilla, Vanilla3D и Yellow. Эти темы можно посмотреть на рисунке ниже.
Если мы хотим создать свою тему, то нужно проделать следующее:
public ActionResult CreateChart()
{
       const string Blue = @"<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"" />
                    </Legends>
                    <BorderSkin SkinStyle=""Emboss"" />
                    </Chart>";
       var chart = new SimpleChart.Chart(width: 700, height: 300, theme: Blue)
       .AddTitle("График посещений")
       .AddSeries(
             name: "Моя программа",
             chartType: "Line",
             xValue: new[] { "Peter", "Andrew", "Julie", "Mary", "Dave" },
             yValues: new[] { "2", "6", "4", "5", "3" })
       .AddLegend()
       .Write();
                   

       return null;
}
Согласитесь, не очень удобно рисовать такие темы руками. Результат вы можете увидеть ниже на рисунке.

Также сложности вызывает то, что много значений, например, тип графика chartType, использует строковое значение. Для создания простых графиков этот тип контрола можно использовать, но если нужно построить нечто серьезнее, то лучше воспользоваться контролом с пространства имен System.Web.DataVisualization. На этой позитивной ноте завершаю статью. Надеюсь, материал вам пригодится и позволит сэкономить время на создание графиков на ASP.NET MVC.  

No comments:

Post a Comment