C#中的堆叠柱状图

3
我正在尝试设置我的程序,以便用户可以显示一个堆积柱状图,显示每个部门中出现的拒绝类别数量(例如,在第1个部门中出现了5个,第2个部门中出现了3个等)。我在网上搜索并自己尝试过,但似乎无法使其正常工作。如果有人能够帮助,那就太棒了。
当用户按下按钮切换到堆积柱状图时,图表当前所做的事情: Stacked Column Chart 代码:
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;
    }
1个回答

8
创建堆叠图很容易。只要你有合适的数据。
如果你没有合适的数据,创建堆叠图就很难。
有一个规则需要遵循:所有系列中的数据都需要对齐才能进行堆叠!
这听起来很简单,但实际上比人们预期的要棘手得多。
在我们开始举几个例子之前,这里是简单规则的详细信息:
1. 规则 #1 你需要在每个系列中都有一个数据点,以便每个 x 值在任何系列中都有一个数据点。 2. 规则 #2 你的点需要按照升序排列。 3. 规则 #3 为了让你控制显示的点的范围和图表的其他方面,所有 x 值都应该是数字。

这里有一个堆积图的工作示例,请参考this post! 其中的问题是'如何避免间隙?' 但实际上归结为'如何使堆积正常工作?'

请注意,示例使用了在代码中编写的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"]));
}

你会注意到我不仅插入了有助于调试的辅助变量,而且还有第二个查找来保存你需要创建系列并将点添加到它们各自的系列所需的部门。
我将这个创建留给你。如果未找到类别或部门,还需要添加必要的检查。

我假设这个系列需要被更改以显示“dept_id”,另外在charRejections语句的开头添加的额外读取器会创建一个错误,指出它有一些无效的参数。 - iiAaronXiX
是的,我写的只是一个脑海中的建议。额外的读取器是为了为每个部门提供系列名称。因此,您需要根据您的部门命名系列,或使用另一个查找。 - TaW
我知道我可能在这方面是个新手,但我以前从未使用过堆叠柱状图,而且这个答案只让我更加困惑。抱歉。 - iiAaronXiX
很抱歉让你感到困惑。我已经改变了答案,但是请注意,新的答案更长,并且会涉及到你需要知道的各种细节,因此可能仍然会使你感到困惑。在研究并实施查找结构后,请随时回来提问。 - TaW
我已经修改了答案,使用了InsertEmptyPoints函数的稍微简单一些的重载。 - TaW

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接