C#中的堆积柱形图 [英] Stacked Column Chart in C#

查看:103
本文介绍了C#中的堆积柱形图的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试设置我的程序,以便用户可以显示一个堆积柱形图,该图显示每个部门中出现的拒绝类别的数量(例如,部门 1 中出现 5 个,部门 2 中出现 3 个,等等).我在网上环顾四周并自己尝试了一下,但我似乎无法让它工作.如果有人能够提供帮助,那就太棒了.

当用户按下按钮切换到堆积柱形图时,图表当前会做什么:

代码:

private void btnStacked_Click(object sender, EventArgs e){charRejections.Series["RFR"].Enabled = false;charRejections.Series["部门 1"].Points.Clear();charRejections.Series["部门 1"].Enabled = true;charRejections.Series["部门 2"].Points.Clear();charRejections.Series["部门 2"].Enabled = true;charRejections.Series["部门 3"].Points.Clear();charRejections.Series["部门 3"].Enabled = true;{string connectiontring = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=|DataDirectory|\Database1.mdb";DataConnection = new OleDbConnection(connectiontring);尝试{数据连接.Open();OleDbCommand 命令 = new OleDbCommand();command.Connection = 数据连接;string query1 = "SELECT COUNT(reject_category) 作为拒绝,reject_category FROM tblReject_test GROUP BY reject_category";command.CommandText = query1;OleDbDataReader reader = command.ExecuteReader();而 (reader.Read()){charRejections.Series["部门 1"].Points.AddXY(reader["reject_category"].ToString(), reader["reject"].ToString());charRejections.Series["部门 2"].Points.AddXY(reader["reject_category"].ToString(), reader["reject"].ToString());charRejections.Series["部门 3"].Points.AddXY(reader["reject_category"].ToString(), reader["reject"].ToString());}数据连接.关闭();}捕获(异常前){MessageBox.Show("错误" + ex);}}this.charRejections.Series["部门 1"].ChartType = System.Windows.Forms.DataVisualization.Charting.SeriesChartType.StackedColumn;this.charRejections.Series["部门 2"].ChartType = System.Windows.Forms.DataVisualization.Charting.SeriesChartType.StackedColumn;this.charRejections.Series["部门 3"].ChartType = System.Windows.Forms.DataVisualization.Charting.SeriesChartType.StackedColumn;}

解决方案

创建堆叠图表很容易.前提是您拥有适当的数据.

创建堆叠图表很难.如果您没有正确的数据.

有一个规则要遵循:所有系列中的数据都需要对齐才能使堆叠起作用!

这听起来很简单,但实际上比人们想象的要复杂得多.

在我们开始举几个例子之前,这里是简单规则分解的细节:

  • 规则 #1 您需要在 **each 系列中为您在任何系列中拥有的 每个 x 值设置一个数据点.**

  • 规则 #2 您的点需要按正确的顺序排列,即 x 值升序

  • 规则 3 为了让您控制要显示的点范围和图表的其他方面,所有 x 值都应为数字

有关堆叠图表的工作示例,请查看 这篇文章!那里的问题是'如何避免间隙?',但它实际上归结为'如何使堆叠正常工作?'.

请注意,该示例使用来自以代码编写的 DataSource 的数据.像您一样从数据库中读取它没有真正的区别.

解决方案是添加虚拟点来填补空白.

这将是您的问题之一,因为您不能期望查询中的数据是完整的.

要解决此问题,您可以将查询更改为某些 join 以填补空白,或者请图表帮助您.

我不会进入 SQL 选项,尽管它似乎是最自然的选项.但是请注意,为了遵循规则 #2,您需要在查询中添加一个 order 子句,以便在任何情况下都按 x 值对记录进行排序,即按拒绝类别.

让我们来看看一个有趣的辅助函数,名为 Chart.DataManipulator.InsertEmptyPoints :

这个函数有几个重载;我们将使用包含我们想要对齐的所有系列名称的字符串的字符串.这不仅会添加缺失的点,还会实际将它们插入缺失的位置,所以我们现在应该可以通过规则 #1 &2!

在讨论更多细节之前(是的,更多的细节,叹息,但有很多地方要做正确..)让我们来看看规则 #3:

这是一种适用于所有图表类型的图表,也是图表控件用户最常中断的一种规则,通常甚至没有注意到..:

所有 X 值都应该是数字!

如果改为添加字符串,这些字符串将被填充到轴标签中,否则将丢弃.最值得注意的是,结果数据点的 x 值都是 0

只要您不需要它们就可以,但是一旦您使用它们,您就会遇到令人讨厌的惊喜.由于它们已消失,因此您无法使用它们来计算任何内容、格式化标签、显示工具提示等,或使用它们设置要显示的范围.

请注意,即使 x 值都是 0,数据点仍然沿 x 轴分布;你只是不再控制它了..

所以你需要决定一些方案来把你的 x 值变成数字!

一种是建立一个数据结构,其中列出了所有值:

 列表<字符串>catLookup = new List() { "ARTEFACT", "et..cetc.."};

然后您可以像这样找到每个值:

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

这会起作用,但如果您的应用程序是真实的,它应该能够随着数据增长;所以你应该从数据库中读取所有可能的值.如果设计得当,已经有一个查找表可以使用,使用它的键将是最自然的选择.

如果不是,您仍然可以通过简单的查询读取值:

通过reject_category从tblReject_test订单中选择不同的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"]));}

您会注意到,我不仅插入了使调试更容易的辅助变量,而且还进行了第二次查找以保存创建系列所需的部门并将点添加到各自的系列中..

我把这个留给你;如果未找到类别或部门,还添加必要的检查..

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). 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.**

  • Rule #2 Your points need to be in the right order, that is with ascending x-values

  • Rule #3 To let you control the range of points to display and other aspects of your chart, all x-values should be numeric

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

I won't go into the SQL option although it would seem to be the most natural one. 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, i.e. by your rejection-categories.

Let's instead have a look at an interesting helper function called 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!

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:

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!

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!

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; you just don't control that any more..

So you need to decide on some scheme to turn your x-values into numbers!

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

这篇关于C#中的堆积柱形图的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆