Создание диаграмм

128

Одной из наиболее распространенных графических задач является построение диаграммы. Элемент управления Chart в ASP.NET предлагает широкий набор типов диаграмм и параметров конфигурации. Элемент управления Chart был доступен как загружаемый дополнительно в .NET версии 3.5 SP1, но теперь входит в состав .NET 4.0.

Многообразие функциональных средств и параметров этого элемента управления столь велико, что их просто невозможно осветить в одной статье. В последующих разделах будет показано, как создавать некоторые из доступных типов диаграмм, привязывать диаграммы к различным источникам данных и выполнять ряд полезных функций создания диаграмм. С более полным набором примеров можно ознакомиться в библиотеке образцов, которая содержит свыше 200 примеров различных элементов управления Chart.

Создание базовой диаграммы

Как и при изучении многих аспектов ASP.NET, ознакомление с элементом управления Chart лучше всего начать с примера. На рисунке ниже показана простая диаграмма. Мы рассмотрим последовательность действий для создания этой диаграммы, а затем ряд различных способов ее индивидуальной настройки и адаптации. Чтобы было легче понять работу элемента управления Chart, рисунок снабжен надписями, указывающими основные элементы.

Простой элемент управления Chart

Диаграмму можно добавить, перетаскивая элемент управления Chart из панели инструментов (он находится в группе Data (Данные)) на поверхность визуального конструктора или помещая дескриптор <asp:Chart> в код разметки. Объявление для этого примера диаграммы выглядит так:

<asp:Chart ID="Chart1" runat="server">
    <ChartAreas>
        <asp:ChartArea Name="ChartArea1" />
    </ChartAreas>
</asp:Chart>

Каждая диаграмма имеет одну или более областей, в которых можно отображать данные. В приведенном выше базовом объявлении определена одна диаграмма, которая будет использоваться для графического отображения двух наборов данных. Как и в случае большинства элементов управления ASP.NET, для работы с диаграммой можно применять файл отделенного кода или продолжать пользоваться кодом разметки. Вначале рассмотрим подход с отделенным кодом. Все начинается с настройки внешнего вида диаграммы (чтобы файл кода можно было использовать с элементом управления Chart, должно быть импортировано пространство имен System.Web.UI.DataVisualization.Charting):

protected void Page_Load(object sender, EventArgs e)
{
        // Форматировать диаграмму
        Chart1.BackColor = Color.Gray;
        Chart1.BackSecondaryColor = Color.WhiteSmoke;
        Chart1.BackGradientStyle = GradientStyle.DiagonalRight;

        Chart1.BorderlineDashStyle = ChartDashStyle.Solid;
        Chart1.BorderlineColor = Color.Gray;
        Chart1.BorderSkin.SkinStyle = BorderSkinStyle.Emboss;

        // Форматировать область диаграммы
        Chart1.ChartAreas[0].BackColor = Color.Wheat;
        
        // Добавить и форматировать заголовок
        Chart1.Titles.Add("ASP.NET Chart");
        Chart1.Titles[0].Font = new Font("Utopia", 16);
}

Фон диаграммы - это пространство, которое окружает область диаграммы. В качестве фона диаграммы была выбрана градиентная заливка с диагональным переходом от серого цвета (Gray) до белого дыма (WhiteSmoke), что было указано с помощью свойств BackColor, BackSecondaryColor и BackGradientStyle. При визуализации диаграммы фон создается с применением выбранной цветовой схемы.

После этого в примере выше мы задаем стиль рамки. Свойство BorderlineDashStyle управляет отображением края рамки. Значение Solid из перечисления ChartDashStyle обеспечивает рисование вокруг края диаграммы линии толщиной в один пиксель цветом, заданным в свойстве BorderlineColor. в качестве которого был установлен цвет Gray. Свойство BorderSkin поддерживает множество опций, но в примере была установлена только опция SkinStyle в Emboss, что создает трехмерный эффект приподнятой рамки с тенью.

Следующий шаг - выбор цвета фона для области диаграммы и определение заголовка. Обратите внимание, что ссылка на область диаграммы выполняется по индексному значению. Это связано с тем, что диаграмма может иметь более одной области, в которой могут графически представляться данные. Решение этой задачи рассматривается далее в статье. Диаграмма имеет только одну область, и для нее выбран цвет Wheat (пшеничный).

Диаграмма может иметь более одного заголовка, поэтому новый заголовок понадобится добавить в коллекцию Titles с помощью метода Add(), а затем сослаться на нужный заголовок по индексу для его форматирования. Форматирование сводится к установке шрифта и размера. Диаграмма с выполненными на данный момент настройками показана на рисунке ниже:

Сформатированная, но пустая диаграмма

Если для тестирования сайтов ASP.NET вы используете IIS, а не встроенный в Visual Studio сервер разработки IIS Express, то в файле web.config придется добавить следующие настройки HTTP-обработчика для диаграмм:

<configuration>
  ...
  <appSettings>
    <add key="ChartImageHandler" value="storage=file;timeout=20;" />
  </appSettings>
  <system.webServer>
    <handlers>
      <add name="ChartImg" verb="*" path="ChartImg.axd" 
           type="System.Web.UI.DataVisualization.Charting.ChartHttpHandler, System.Web.DataVisualization, 
                Version=4.0.0.0, Culture=neutral"  />
    </handlers>
  </system.webServer>
</configuration>

Существует множество различных способов форматирования диаграммы, и выбор подходящего набора опций может требовать некоторого времени. Мы начали с задания внешнего вида диаграммы, поскольку по странной иронии процесс добавления данных проще подбора подходящей цветовой схемы. Элемент управления Chart настолько гибкий и обладает таким многообразием опций, что бремя выбора само по себе становится достаточно серьезным препятствием.

Как только основные настройки представления данных определены, можно заняться самими данными. Наборы данных представлены с использованием класса Series, и каждый создаваемый класс Series должен быть добавлен в коллекцию Chart.Series с помощью метода Add(). Обычно создание и добавление класса Series осуществляется в одном шаге, как показано в следующем фрагменте кода:

protected void Page_Load(object sender, EventArgs e)
{
        // ...
        
        Chart1.Series.Add(new Series("ColumnSeries")
        {
            ChartType = SeriesChartType.Column
        });
}

В этом коде создается новая серия ColumnSeries и за счет установки свойства ChartType в SeriesChartType.Column указывается, что данные этой последовательности должны быть представлены в виде столбчатой диаграммы. Свойство SeriesChartType принимает 35 различных значений, каждое из которых соответствует отдельной разновидности диаграммы. Если значение свойства ChartType не указано, по умолчанию для него выбирается Column (столбчатая диаграмма).

Для наборов данных можно сконфигурировать ряд свойств. Ниже приведен код, который используется для создания второй коллекции Series и содержит некоторые изменения:

Chart1.Series.Add(new Series("SplineSeries")
{
    ChartType = SeriesChartType.Spline,
    BorderWidth = 3,
    ShadowOffset = 2,
    Color = Color.PaleVioletRed
});

Для этой диаграммы в качестве типа был выбран Spline (график в виде плавной линии). Значение BorderWidth управляет толщиной линейного графика, а установка значения для свойства ShadowOffset создает эффект наличия трехмерной тени под вычерчиваемой линией. Свойство Color определяет цвет, используемый для рисования графика. Для этого примера был выбран бросающийся в глаза оттенок PaleVioletRed.

Последний шаг состоит в добавлении данных в набор. Существует множество различных способов связывания данных с диаграммой. В этом примере применяется простейший из них - перечисление статических значений:

Chart1.Series[0].Points.DataBindY(
    new int [] { 5, 3, 12, 14, 11, 7, 3, 5, 9, 12, 11, 10 });
        
Chart1.Series[1].Points.DataBindY(
    new int [] { 3, 7, 13, 2, 7, 15, 23, 20, 1, 5, 7, 6 });

Каждая коллекция Series содержит коллекцию Points. Мы использовали метод DataBindY(), реализующий интерфейс IEnumerable, который позволяет для указания данных применять массив целочисленных значений. Индекс данных используется в качестве X-координаты для каждого значения Y в массиве. При загрузке страницы, содержащей элемент управления Chart, результат выглядит, как показано на первом рисунке в статье.

Конфигурирование элемента управления Chart можно полностью выполнить также с помощью разметки. Определение рассмотренной диаграммы в этой форме имеет следующий вид:

<asp:Chart ID="Chart1" runat="server" Width="600px" BackColor="Gray"
    BackSecondaryColor="WhiteSmoke" BackGradientStyle="DiagonalRight" 
    BorderlineDashStyle="Solid" BorderlineColor="Gray">
    <BorderSkin SkinStyle="Emboss" />
    <Titles>
        <asp:Title Text="ASP.NET Chart" Font="Utopia,16" />
    </Titles>
    <Series>
        <asp:Series Name="ColumnSeries" ChartType="Column">
            <Points>
                <asp:DataPoint YValues="5" />
                <asp:DataPoint YValues="3" />
                <asp:DataPoint YValues="12" />
                <asp:DataPoint YValues="14" />
                <asp:DataPoint YValues="11" />
                <asp:DataPoint YValues="7" />
                <asp:DataPoint YValues="3" />
                <asp:DataPoint YValues="5" />
                <asp:DataPoint YValues="9" />
                <asp:DataPoint YValues="12" />
                <asp:DataPoint YValues="11" />
                <asp:DataPoint YValues="10" />
            </Points>
        </asp:Series>
        <asp:Series Name="SplineSeries" ChartType="Spline" BorderWidth="3" ShadowOffset="2"
            Color="PaleVioletRed">
            <Points>
                <asp:DataPoint YValues="3" />
                <asp:DataPoint YValues="7" />
                <asp:DataPoint YValues="13" />
                <asp:DataPoint YValues="2" />
                <asp:DataPoint YValues="7" />
                <asp:DataPoint YValues="15" />
                <asp:DataPoint YValues="23" />
                <asp:DataPoint YValues="20" />
                <asp:DataPoint YValues="1" />
                <asp:DataPoint YValues="5" />
                <asp:DataPoint YValues="7" />
                <asp:DataPoint YValues="6" />
            </Points>
        </asp:Series>
    </Series>
    <ChartAreas>
        <asp:ChartArea Name="ChartArea1" BackColor="Wheat" />
    </ChartAreas>
</asp:Chart>

Вот и все. Если теперь взглянуть на веб-страницу, результат будет выглядеть, как показано на первом рисунке в статье.

Мы не станем подробно останавливаться на всех доступных опциях представления диаграмм, но одна из обычно встречающихся задач - генерация трехмерных диаграмм. Чтобы преобразовать нашу простую диаграмму в трехмерную, добавим следующую строку кода в метод Page_Load:

Chart1.ChartAreas[0].Area3DStyle.Enable3D = true;

Активизация трехмерного представления производится на уровне области диаграммы (ChartArea). Если диаграмма содержит несколько областей, двухмерные и трехмерные графики можно сочетать. Элементарная диаграмма в трехмерном представлении показана на рисунке ниже. А вот изменение, которое должно быть внесено в код разметки:

<ChartAreas>
    <asp:ChartArea Name="ChartArea1" BackColor="Wheat" Area3DStyle-Enable3D="true" />
</ChartAreas>
Пример диаграммы в трехмерном представлении

Заполнение диаграммы данными

Кроме статических данных, которые использовались в предыдущем примере, элемент управления Chart может быть заполнен данными из множества различных источников. В настоящем разделе мы рассмотрим наиболее часто используемые источники данных.

Привязка к таблице базы данных

В этом примере для демонстрации привязки диаграммы к таблице базы данных используется таблица Products тестовой базы данных Northwind. Процесс начинается с форматирования элемента управления Chart, во многом подобно тому, как это делалось в предыдущем примере.

Для определения соединения с базой данных Northwind с использованием свойства ConnectionString применяются стандартные классы ADO.NET из пространства имен System.Data.SqlClient. После этого определяется запрос, который будет использоваться для генерации данных в диаграмме. Дня этого примера был выбран запрос столбцов ProductName и UnitsInStock таблицы Products с выбором товаров, которые не были сняты с производства, при этом результаты ограничиваются пятью строками. Затем открывается соединение и создается объект SqlDataReader - все это стандартный код ADO.NET:

protected void Page_Load(object sender, EventArgs e)
{        
        // Форматировать диаграмму
        Chart1.BackColor = Color.Gray;
        Chart1.BackSecondaryColor = Color.WhiteSmoke;
        Chart1.BackGradientStyle = GradientStyle.DiagonalRight;

        Chart1.BorderlineDashStyle = ChartDashStyle.Solid;
        Chart1.BorderlineColor = Color.Gray;
        Chart1.BorderSkin.SkinStyle = BorderSkinStyle.Emboss;

        // Форматировать область диаграммы
        Chart1.ChartAreas[0].BackColor = Color.Wheat;
        
        // Добавить и форматировать заголовок
        Chart1.Titles.Add("ASP.NET Chart");
        Chart1.Titles[0].Font = new Font("Utopia", 16);

        // Создать соединение с базой данных
        SqlConnection conn = new SqlConnection(
            ConfigurationManager.ConnectionStrings["Northwind"].ConnectionString) ;
        
        // Определить команду
        SqlCommand command = new SqlCommand("SELECT TOP (5) ProductName, UnitsInStock " 
            + "FROM Products WHERE (Discontinued = 'FALSE')", conn);
        
        // Открыть команду и создать объект чтения
        command.Connection.Open();
        SqlDataReader reader = command.ExecuteReader();

        // Очистить серии диаграммы и привязаться к таблице
        Chart1.Series.Clear();
        Chart1.DataBindTable(reader);
        Chart1.Series["UnitsInStock"].ChartType = SeriesChartType.StackedBar;
        
        // Закрыть объект чтения и соединение
        reader.Close();
        conn.Close();
}

В этом коде, после того как диаграмма сформатирована, а соединение с данными создано, данные привязываются к диаграмме. Процесс начинается с очистки коллекции Chart.Series. Это необходимо потому, что при привязке таблицы элемент управления Chart автоматически создает новый экземпляр Series для каждого числового столбца, найденного в данных. В результате будет создан экземпляр Series по имени UnitsInStock. Однако созданный по умолчанию экземпляр Series (получивший имя Series1) не удаляется, а это может привести к проблемам при изменении значения свойства Series.ChartType на такое, которое не сможет быть графически представлено в той же области ChartArea, что и столбчатая диаграмма (Column), являющаяся стандартным типом для серий по умолчанию. Рекомендуется перед привязкой к данным созданный по умолчанию класс Series удалить, чтобы в конечном итоге остался только экземпляр Series, ассоциированный с данными.

Затем нужно выполнить привязку с использованием метода DataBindTable(), который принимает ранее созданный SqlDataReader в качестве аргумента. Как уже упоминалось ранее, привязка к таблице автоматически создает по одной серии данных для каждого числового столбца данных. В данном случае запрашиваются столбцы ProductName и UnitsInStock. Элемент управления Chart проигнорирует текстовый столбец ProductName и создаст новый экземпляр Series для столбца UnitsInStock. Форматирование объекта Series невозможно до тех пор, пока он не будет создан, поэтому свойство ChartType устанавливается после того, как таблица привязана к элементу управления. И, наконец, объекты SqlDataReader и SqlCommand закрываются.

Диаграмма, созданная этим кодом, показана на рисунке ниже:

Диаграмма, привязанная к таблице базы данных

Проблема с этой диаграммой состоит в том, что столбец ProductName был проигнорирован. Во время процесса привязки элемент управления Chart работал исходя из того, что все SQL-данные должны были интерпретироваться как значения по оси Y, и отбрасывал все, что не могло использоваться подобным образом.

Если один из столбцов должен служить в качестве значений по оси X, об этом нужно явно сообщить элементу управления Chart, выполняя привязку данных с помощью метода Points.DataBindXY() класса Series. Чтобы адаптировать предыдущий пример, закомментируйте три строки начиная с Chart1.Series.Clear() и добавьте две новых строки в файл отделенного кода:

// Очистить серии диаграммы и привязаться к таблице
/* Chart1.Series.Clear();
Chart1.DataBindTable(reader);
Chart1.Series["UnitsInStock"].ChartType = SeriesChartType.StackedBar; */
// Привязать значения X и Y к созданным по умолчанию сериям и форматировать диаграмму
Chart1.Series.Add(new Series("Default")
{
    ChartType = SeriesChartType.StackedBar
});
Chart1.Series[0].Points.DataBindXY(reader, "ProductName", reader, "UnitsInStock");

Метод DataBindXY() позволяет предоставлять источник данных и имена столбцов, которые должны применяться для осей X и Y. В рассматриваемом примере источником данных для обеих осей является SqlDataReader, а именами столбцов - ProductName и UnitsInStock. Обратите внимание, что в этот раз коллекция Series не очищается. Метод DataBindXY() применяется к уже существующей коллекции Series (а не создает новую), и в примере была использована та, которая создается по умолчанию. Открыв измененную страницу, можно увидеть результат, показанный на рисунке ниже:

Явная привязка значений X и Y к диаграмме

Привязка к объектному источнику данных

Один из наиболее гибких механизмов заполнения диаграммы предусматривает использование ObjectDataSource, когда определяется объект, который может получать данные, а в качестве моста между логикой извлечения данных в коде и элементом управления Chart служит ObjectDataSource. Класс извлечения должен быть создан в папке App_Code. В следующем примере возвращаются простые статические данные:

public class MyObjectDataSource
{

    public class DataItem
    {
        public string Name { get; set; }
        public double Popularity { get; set; }
    }

    public DataItem[] GetData()
    {
        return new DataItem[] {
            new DataItem() {Name = "Ватрушка", Popularity = 30},
            new DataItem() {Name = "Мороженное", Popularity = 30},
            new DataItem() {Name = "Орешки", Popularity = 20},
            new DataItem() {Name = "Шоколадка", Popularity = 20}
        };
    }
}

В этом примере определен класс DataItem, имеющий свойства Name и Popularity. Метод GetData() генерирует и возвращает массив элементов DataItem. Эта методика привязки будет также работать с методами, которые возвращают объекты DataSet и DataTable. Гибкость использования ObjectDataSource обусловлена тем, что средства, определяющие источник данных, абстрагируются от диаграммы, что позволяет свободно изменять бизнес-логику. Для работы с данными создается новый экземпляр ObjectDataSource с передачей конструктору имени созданного класса извлечения и имени метода, который должен быть вызван для получения данных. В рассматриваемом примере ими соответственно являются MyObjectDataSource и GetData():

protected void Page_Load(object sender, EventArgs e)
{        
        // Форматировать диаграмму
        Chart1.BackColor = Color.Gray;
        Chart1.BackSecondaryColor = Color.WhiteSmoke;
        Chart1.BackGradientStyle = GradientStyle.DiagonalRight;

        Chart1.BorderlineDashStyle = ChartDashStyle.Solid;
        Chart1.BorderlineColor = Color.Gray;
        Chart1.BorderSkin.SkinStyle = BorderSkinStyle.Emboss;

        // Форматировать область диаграммы
        Chart1.ChartAreas[0].BackColor = Color.Wheat;
        
        // Добавить и форматировать заголовок
        Chart1.Titles.Add("ASP.NET Chart");
        Chart1.Titles[0].Font = new Font("Utopia", 16);

        Chart1.Series.Add(new Series("Default")
        {
            ChartType = SeriesChartType.Pie
        });

        // Создать объектный источник данных
        ObjectDataSource ds = new ObjectDataSource("MyObjectDataSource", "GetData") ;
        
        // Привязать источник к диаграмме
        Chart1.DataSource = ds;
        Chart1.Series[0].XValueMember = "Name";
        Chart1.Series[0].YValueMembers = "Popularity";
}

После создания ObjectDataSource привязывается к элементу управления через свойство Chart.DataSource. На этот момент источник данных действует в качестве моста между классом извлечения и диаграммой, но диаграмма не знает, что делать с данными, которые доступны. Эта информация предоставляется с использованием свойств Series.XValueMember и Series.YValueMember, которые позволяют определять, какие члены объекта ObjectDataSource должны применяться для осей X и Y. Диаграмма, созданная этим методом, показана на рисунке ниже:

Диаграмма, созданная с использованием объектного источника данных
Пройди тесты
Лучший чат для C# программистов