Столбчатая диаграмма с накоплением в C#

Я пытаюсь настроить свою программу так, чтобы пользователь мог отображать столбчатую диаграмму с накоплением, которая отображает количество категорий отклонения, которые появляются в каждом отделе (например, 5 появляются в отделе 1, 3 в отделе 2 и т. д.). Я посмотрел в Интернете и сам взломал его, но, похоже, я не могу заставить его работать. Если бы кто-нибудь мог помочь, это было бы здорово.

Что в настоящее время делает диаграмма, когда пользователь нажимает кнопку, чтобы переключиться на столбчатую диаграмму с накоплением: Диаграмма с накоплением

Код:

private void btnStacked_Click(object sender, EventArgs e)
    {
        charRejections.Series["RFR"].Enabled = false;

        charRejections.Series["Department 1"].Points.Clear();
        charRejections.Series["Department 1"].Enabled = true;

        charRejections.Series["Department 2"].Points.Clear();
        charRejections.Series["Department 2"].Enabled = true;

        charRejections.Series["Department 3"].Points.Clear();
        charRejections.Series["Department 3"].Enabled = true;
        {
            string connectiontring = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=|DataDirectory|\\Database1.mdb";
            DataConnection = new OleDbConnection(connectiontring);

            try
            {

                DataConnection.Open();
                OleDbCommand command = new OleDbCommand();
                command.Connection = DataConnection;
                string query1 = "SELECT COUNT(reject_category) as reject, reject_category FROM tblReject_test GROUP BY reject_category";
                command.CommandText = query1;


                OleDbDataReader reader = command.ExecuteReader();
                while (reader.Read())
                {
                    charRejections.Series["Department 1"].Points.AddXY(reader["reject_category"].ToString(), reader["reject"].ToString());
                    charRejections.Series["Department 2"].Points.AddXY(reader["reject_category"].ToString(), reader["reject"].ToString());
                    charRejections.Series["Department 3"].Points.AddXY(reader["reject_category"].ToString(), reader["reject"].ToString());
                }

                DataConnection.Close();
            }
            catch (Exception ex)
            {
                MessageBox.Show("Error " + ex);
            }
        }

        this.charRejections.Series["Department 1"].ChartType = System.Windows.Forms.DataVisualization.Charting.SeriesChartType.StackedColumn;
        this.charRejections.Series["Department 2"].ChartType = System.Windows.Forms.DataVisualization.Charting.SeriesChartType.StackedColumn;
        this.charRejections.Series["Department 3"].ChartType = System.Windows.Forms.DataVisualization.Charting.SeriesChartType.StackedColumn;
    }

person iiAaronXiX    schedule 02.03.2016    source источник


Ответы (1)


Создать диаграмму с накоплением очень просто, если у вас есть нужные данные.

Создать диаграмму с накоплением сложно. Это если у вас нет нужных данных.

Есть одно правило, которому нужно следовать: данные во всех ваших сериях должны быть выровнены, чтобы стек работал!

Звучит просто, но на самом деле все намного сложнее, чем можно было ожидать.

Прежде чем мы начнем с нескольких примеров, вот детали простого правила:

  • Правило № 1 У вас должна быть точка данных в **каждом ряду для каждого значения x, которое у вас есть в любом ряду.**

  • Правило № 2 Ваши баллы должны быть в правильном порядке, то есть с возрастающими значениями x

  • Правило № 3. Чтобы вы могли управлять диапазоном отображаемых точек и другими аспектами диаграммы, все значения x должны быть числовыми.

Рабочий пример диаграммы с накоплением см. на этот пост! Там вопрос был "Как избежать пробелов?", но на самом деле он сводился к "Как заставить стек работать правильно?".

Обратите внимание, что в примере используются данные из DataSource, которые были записаны в коде. Нет никакой реальной разницы в том, чтобы читать его из базы данных, как вы это делаете.

Решение состояло в том, чтобы добавить фиктивные точки, чтобы заполнить пробелы.

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

Чтобы решить эту проблему, вы можете либо изменить свой запрос на какой-нибудь join, который заполняет пробелы, либо попросить диаграмму помочь вам в этом.

Я не буду вдаваться в вариант SQL, хотя он кажется наиболее естественным. Однако обратите внимание, что для того, чтобы следовать правилу № 2, вам нужно добавить в запрос предложение order для сортировки записей по значениям x в любом случае, то есть по вашим категориям отклонения.

Вместо этого давайте рассмотрим интересную вспомогательную функцию под названием Chart.DataManipulator.InsertEmptyPoints :

Эта функция имеет несколько перегрузок; мы будем использовать строку, содержащую все имена серий, которые мы хотим выровнять. Это не просто добавит недостающие точки, но фактически вставит их в те места, где они отсутствуют, так что теперь у нас должно быть все в порядке с правилами № 1 и 2!

Прежде чем вдаваться в подробности (да, еще больше деталей, вздох, но там просто нужно кое-что сделать правильно...), давайте взглянем на правило № 3:

Это правило применимо ко всем типам диаграмм, а также одно правило, которое чаще всего нарушают пользователи элемента управления диаграммами, обычно даже не замечая:

Все X-значения должны быть числовыми!

Если вместо этого вы добавите строки, эти строки будут вставлены в метки осей, а в противном случае будут выброшены. В частности, все значения x результирующих точек данных равны 0!

Это нормально, пока они вам не нужны, но как только вы это сделаете, вас ждет неприятный сюрприз. Поскольку их больше нет, вы не можете использовать их для вычисления чего-либо на их основе, или для форматирования меток, или для отображения всплывающих подсказок и т. д., или использовать их для установки отображаемого диапазона.

Обратите внимание, что даже несмотря на то, что все значения x равны 0, точки данных все еще разбросаны по оси x; ты просто больше не контролируешь это..

Итак, вам нужно выбрать какую-то схему, чтобы превратить ваши значения x в числа!

Один из них — настроить структуру данных, в которой перечислены все ваши значения:

 List<string> catLookup = new List<string>() { "ARTEFACT", "et..cetc.."};

Затем вы можете найти каждое значение следующим образом:

 int found = catLookup.FindIndex(x => x == someValueWeSearch);

Это будет работать, но если ваше приложение настоящее, оно должно иметь возможность расти вместе с данными; поэтому вы должны прочитать все возможные значения из базы данных. Если это сделано правильно, уже есть таблица поиска для использования, и использование ее ключа было бы наиболее естественным выбором.

Если это не так, вы все равно можете прочитать значения с помощью простого запроса:

Select distinct reject_category from tblReject_test order by reject_category;

Теперь давайте сделаем вызов, который выравнивает все имеющиеся у нас ряды:

  string seriesNames = String.Join(",", seriesLookup.Keys);
  chart1.DataManipulator.InsertEmptyPoints(1, IntervalType.Number, seriesNames);

Теперь вернемся к исходному коду и тому, что вам нужно там сделать:

Во-первых, все ваши значения являются строками. Итак, вы должны изменить цикл на что-то вроде этого:

while (reader.Read())
{
    string seriesName = reader[1].ToString();
    int seriesIndex   = seriesLookup.FindIndex(x => x == seriesName);
    string catName    = reader[2].ToString();
    int catIndex      = catLookup.FindIndex(x => x == catName);

    charRejections.Series[seriesName ].Points.AddXY(catIndex, 
                                              Convert.ToInt16((reader["reject"]));
}

Вы заметите, что я не только вставил вспомогательную переменную, которая значительно упрощает отладку, но и второй поиск для хранения отделов, необходимых для создания серии, и добавления точек в соответствующие серии.

Я оставляю создание этого вам; также добавление необходимых проверок, если категория или отдел не найдены.

person TaW    schedule 02.03.2016
comment
Я предполагаю, что серию необходимо изменить, чтобы показать dept_id, а также то, что дополнительный считыватель в начале оператора charRejections создает ошибку, говорящую о том, что у него есть недопустимые аргументы. - person iiAaronXiX; 02.03.2016
comment
Да и да. Я написал это как предложение от верхней части моей головы. Дополнительный считыватель предназначен для предоставления названия серии для каждого отдела. Поэтому вам нужно либо назвать серию в соответствии с вашими отделами, либо использовать другой поиск. - person TaW; 02.03.2016
comment
Я знаю, что я, вероятно, новичок в этом, но мне никогда раньше не приходилось работать со столбчатыми диаграммами с накоплением, и все, что сделал этот ответ, - это смутило меня еще больше. Извиняюсь. - person iiAaronXiX; 02.03.2016
comment
Извините, что ввел вас в заблуждение. Я изменил ответ, но позвольте мне предупредить вас, что он намного длиннее, и, поскольку он содержит все детали, которые вам понадобятся, он все равно может вас смутить. После изучения и реализации структур поиска возвращайтесь со своими вопросами. - person TaW; 03.03.2016
comment
Я изменил ответ, чтобы использовать несколько более простую перегрузку функции InsertEmptyPoints. - person TaW; 05.03.2016