简体   繁体   English

C#中的堆积柱形图

[英]Stacked Column Chart in C#

I am trying to set up my program so that the user can display a stacked column chart that displays the number of reject categories that appear in each department (for example, 5 appear in department 1, 3 in department 2, etc).我正在尝试设置我的程序,以便用户可以显示一个堆积柱形图,该图显示每个部门中出现的拒绝类别的数量(例如,部门 1 中出现 5 个,部门 2 中出现 3 个,等等)。 I've had a look around online and taken a crack at it myself but I cannot seem to get it working.我在网上环顾四周并自己尝试了一下,但我似乎无法让它工作。 If anyone would be able to help that would be fantastic.如果有人能够提供帮助,那就太棒了。

What the chart currently does when the user presses a button to switch to a stacked column chart:当用户按下按钮切换到堆积柱形图时,图表当前会做什么: 堆积柱形图

Code:代码:

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

Creating a stacked chart is easy.创建堆积图很容易。 That is if you have the proper data.也就是说,如果你有正确的数据。

Creating a stacked chart is hard.创建堆积图很难。 That is if you don't have the proper data.也就是说,如果您没有正确的数据。

There is one rule to follow: The data in all your series need to be aligned for the stacking to work!有一条规则要遵循:所有系列中的数据都需要对齐才能进行堆叠!

This sounds simple but in fact is a lot trickier than one may expect.这听起来很简单,但实际上比人们想象的要复杂得多。

Before we start with a few examples here are the details the simple rule breaks down to:在我们开始举几个例子之前,这里是简单规则分解为的细节:

  • Rule #1 You need to have a datapoint in **each series for each x-value you have in any series.**规则 #1 对于任何系列中的每个x 值,您都需要在 **每个系列中有一个数据点。**

  • Rule #2 Your points need to be in the right order, that is with ascending x-values规则 #2您的点需要按正确的顺序排列,即 x 值升序

  • Rule #3 To let you control the range of points to display and other aspects of your chart, all x-values should be numeric规则 #3 为了让您控制要显示的点范围和图表的其他方面,所有 x 值都应该是数字

For a working example of a stacked chart do have a look at this post !有关堆叠图表的工作示例,请查看这篇文章 There the question was 'How to avoid the gaps?'问题是“如何避免差距?” but it really boiled down to 'How to make the stacking work properly?'但它真的归结为“如何使堆叠正常工作?” . .

Note that the example uses data from a DataSource that was written in code.请注意,该示例使用来自以代码编写的 DataSource 的数据。 There is no real difference to reading it from a DataBase as you are doing.像您一样从数据库中读取它没有真正的区别。

The solution was to add dummy points to fill in the gaps.解决方案是添加虚拟点来填补空白。

This will be one of your problems as you can't expect the data from your query to be complete.这将是您的问题之一,因为您不能期望查询中的数据是完整的。

To solve this problem you can either change your query to some join that fills the gaps or ask the chart to help you along.要解决此问题,您可以将查询更改为某些join以填补空白,或者请图表帮助您。

I won't go into the SQL option although it would seem to be the most natural one.我不会进入 SQL 选项,尽管它似乎是最自然的选项。 Note however that in order to follow rules #2 you will need to add an order clause to the query to sort the records by the x-values in any case, ie by your rejection-categories.但是请注意,为了遵循规则#2,您需要在查询中添加一个order子句, order在任何情况下都按 x 值对记录进行排序,即按您的拒绝类别。

Let's instead have a look at an interesting helper function called Chart.DataManipulator.InsertEmptyPoints :让我们来看看一个有趣的辅助函数Chart.DataManipulator.InsertEmptyPoints

This function has several overloads;这个函数有几个重载; we'll use the one with the string that holds all series names that we want to align.我们将使用包含我们想要对齐的所有系列名称的字符串的字符串。 This will not just add the missing points but actually insert them at the spots where they are missing, so we should now be ok by rules #1 & 2!这不仅会添加缺失的点,而且实际上将它们插入到缺失的位置,所以我们现在应该按照规则 #1 和 2 确定!

Before going into more details (yes, even more details, sigh, but there simply is quite a bit to get right..) let's have a look at rule #3:在进入更多细节之前(是的,更多细节,叹息,但只是有很多地方要正确......)让我们看看规则#3:

This is one that applies to all chart types and also the one rule users of the chart control break most often, usually without even noticing..:这是一种适用于所有图表类型的图表,也是图表控件用户最常中断的一种规则,通常甚至没有注意到..:

All X-Values should be numeric!所有 X 值都应该是数字!

If instead you add strings, those string will be stuffed into the axis labels and otherwise are thrown away .如果改为添加字符串,则这些字符串将被填充到轴标签中,否则将被丢弃 Most notably the x-values of the resulting data points all are 0 !最值得注意的是,结果数据点的 x 值都是0

Which is ok as long as you don't need them, but once you do you are in for a nasty surprise.只要您不需要它们就可以,但是一旦您使用它们,您就会遇到令人讨厌的惊喜。 Since they are gone you cannot use them to calculate anything from them, or to format the labels, or to show tooltips etc, or use them to set a range to display.由于它们已经消失,您不能使用它们来计算它们的任何内容,或格式化标签,或显示工具提示等,或使用它们来设置要​​显示的范围。

Note that even though the x-values are all 0 the datapoints are still spread along the x-axis;请注意,即使 x 值都为0 ,数据点仍沿 x 轴分布; you just don't control that any more..你只是不再控制它了..

So you need to decide on some scheme to turn your x-values into numbers!所以你需要决定一些方案来把你的 x 值变成数字!

One is to set up a data structure where all your values are listed:一种是建立一个数据结构,其中列出了所有值:

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

You can then find each value like this:然后,您可以像这样找到每个值:

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

This will work but if your application is real, it should be able to grow with the data;这会起作用,但如果您的应用程序是真实的,它应该能够随着数据增长; so you should read all possible values from the database.所以你应该从数据库中读取所有可能的值。 If it is desigend properly there is already a lookup table to use and using its key would be the most natural choice.如果设计得当,已经有一个查找表可以使用,使用它的键将是最自然的选择。

If it isn't, you still can read in the values with a simple query:如果不是,您仍然可以使用简单的查询读取值:

Select distinct reject_category from tblReject_test order by reject_category;

Now let's do the call that aligns all series we have:现在让我们调用对齐我们拥有的所有系列:

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

Now back to your original code and what you need to do there:现在回到您的原始代码以及您需要在那里做什么:

For one thing all your values are strings.一方面,您的所有值都是字符串。 So you should change you loop to something like this:所以你应该把循环改成这样:

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

You will notice that I not only have inserted helper variable that make debugging so much easier, but also a second lookup to hold the departments you need to create the series and add the points to their respective series..您会注意到,我不仅插入了使调试变得更加容易的辅助变量,而且还进行了第二次查找以保存创建系列所需的部门并将点添加到各自的系列中。

I leave creating this to you;我把创造这个留给你; also adding the necessary checks if a category or department is not found..如果未找到类别或部门,还添加必要的检查..

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM