【问题标题】:C#/Excel: Working Around Maximum Series Size On ChartC#/Excel:在图表上处理最大系列大小
【发布时间】:2009-09-14 17:02:35
【问题描述】:

我需要帮助以编程方式绘制超出单个 Excel 系列的点数。

根据http://office.microsoft.com/en-us/excel/HP100738491033.aspx,Excel 2007 图表上可显示的最大点数为 256000。鉴于每个系列的上限为 32000 个点,因此需要 8 个系列来绘制全部 256000 个点。由于我们使用的数据集很大,我的客户需要绘制每个图表的最大点数。

我在 C#/Excel 互操作方面有中等经验,所以我认为以编程方式创建工作表然后循环遍历每组 32000 个点并将它们作为一个系列添加到图表中会很容易,当数据完全绘制时停止或 8 个系列被绘制。如果着色得当,8 系列在视觉上与单个系列没有区别。

不幸的是,我在这里。我遇到的主要问题是:

(full size) The maximum number of datapoints you can use in a data series for a 2-D chart is 32,000... http://img14.imageshack.us/img14/9630/errormessagen.png

这个弹出窗口,奇怪的是,当我执行该行时出现:

并附有:

Exception from HRESULT: 0x800AC472 http://img21.imageshack.us/img21/5153/exceptionb.png

在我指定要绘制的数据之前,我不明白如何生成这样的弹出/警告/异常。 Excel 在这里是不是想变聪明?

作为临时解决方法,我已将 chart.ChartType = chartType 语句放入 try-catch 块中,以便继续进行。

如下所示,我的“分块”代码按预期工作,但在尝试向图表添加数据时仍然遇到同样的问题。 Excel 说我试图绘制太多点,而我显然不是。

(full size image) code block with watch window http://img12.imageshack.us/img12/5360/snippet.png

我知道我可能还没有与每个系列正确关联的 X 值,但我正在努力让这个工作在我更进一步之前发挥作用。

任何帮助将不胜感激。

这是完整的代码:

public void DrawScatterGraph(string xColumnLetter, string yColumnLetterStart, string yColumnLetterStop, string xAxisLabel, string yAxisLabel, string chartTitle, Microsoft.Office.Interop.Excel.XlChartType chartType, bool includeTrendline, bool includeLegend)
    {
        int totalRows = dataSheet.UsedRange.Rows.Count; //dataSheet is a private class variable that 
                                                        //is already properly set to the worksheet
                                                        //we want to graph from

        if (totalRows < 2) throw new Exception("Not generating graph for " + chartTitle.Replace('\n', ' ') 
                                            + " because not enough data was present");

        ChartObjects charts = (ChartObjects)dataSheet.ChartObjects(Type.Missing);
        ChartObject chartObj = charts.Add(100, 300, 500, 300);
        Chart chart = chartObj.Chart;

        try { chart.ChartType = chartType; }
        catch { }   //i don't know why this is throwing an exception, but i'm
                    //going to bulldoze through this problem temporarily 

        if (totalRows < SizeOfSeries) //we can graph the data in a single series - yay!
        {
            Range xValues = dataSheet.get_Range(xColumnLetter + "2", xColumnLetter + totalRows.ToString());
            Range yValues = dataSheet.get_Range(yColumnLetterStart + "1", yColumnLetterStop + totalRows.ToString());
            chart.SetSourceData(yValues, XlRowCol.xlColumns);
            SeriesCollection seriesCollection = (SeriesCollection)chart.SeriesCollection(Type.Missing);
            foreach (Series s in seriesCollection)
            {
                s.XValues = xValues;
            }
        }
        else // we need to split the data across multiple series -- this doesn't work yet
        {
            int startRow = 1; 
            while (startRow < totalRows)
            {
                int stopRow = (startRow + SizeOfSeries)-1;  
                if (stopRow > totalRows) stopRow = totalRows;
                Range curRange = dataSheet.get_Range(yColumnLetterStart + startRow.ToString(), yColumnLetterStop + stopRow.ToString());
                try
                {
                    ((SeriesCollection)chart.SeriesCollection(Type.Missing)).Add(curRange, XlRowCol.xlColumns, 
                                                                            Type.Missing, Type.Missing, Type.Missing);
                }
                catch (Exception exc)
                {
                    throw new Exception(yColumnLetterStart + startRow.ToString() + "!" + yColumnLetterStop + stopRow.ToString() + "!" + exc.Message);
                }
                startRow = stopRow+1;
            }
        }

        chart.HasLegend = includeLegend;
        chart.HasTitle = true;
        chart.ChartTitle.Text = chartTitle;

        Axis axis;
        axis = (Axis)chart.Axes(XlAxisType.xlCategory, XlAxisGroup.xlPrimary);
        axis.HasTitle = true;
        axis.AxisTitle.Text = xAxisLabel;
        axis.HasMajorGridlines = false;
        axis.HasMinorGridlines = false;

        axis = (Axis)chart.Axes(XlAxisType.xlValue, XlAxisGroup.xlPrimary);
        axis.HasTitle = true;
        axis.AxisTitle.Text = yAxisLabel;
        axis.HasMajorGridlines = true;
        axis.HasMinorGridlines = false;

        if (includeTrendline)
        {
            Trendlines t = (Trendlines)((Series)chart.SeriesCollection(1)).Trendlines(Type.Missing);
            t.Add(XlTrendlineType.xlLinear, Type.Missing, Type.Missing, 0, 0, Type.Missing, false, false, "AutoTrendlineByChameleon");
        }

        chart.Location(XlChartLocation.xlLocationAsNewSheet, "Graph");
    }

【问题讨论】:

    标签: c# excel charts series


    【解决方案1】:

    如果活动单元格位于数据块中,Excel 可能会假定您要绘制范围。

    选择一个不在数据旁边的空白单元格,然后插入图表。它将是空白的,而不是预先填充的。

    【讨论】:

    • 谢谢乔恩!我在函数顶部附近添加了以下内容: Range tempRange = dataSheet.get_Range("E1", "E2"); tempRange.Select();其中 E 列为空(我的数据仅在 A - C 列中)。有了这个变化,一切都正常工作。再次感谢您!
    【解决方案2】:

    您的图表真的必须在 Excel 中吗?有了这么多数据点,性能将是可怕的。

    一个建议可能是使用第三方组件来生成图表。实现这一点的具体技术取决于您是否必须能够在 excel 中查看数据,或者输出图表是否只需要在其他地方可用。

    如果图表不需要在 Excel 中可见,则只需传递数据点并在绘图应用程序或网络浏览器中查看图像。

    如果您确实需要使用 excel 查看图表,您可以调用外部绘图应用程序并将一组数据点传递给它。当它返回图像时,只需使用 vba 将其插入到 excel 中。

    如果您需要,我可以为您提供有关这两种方法的更多信息。

    此外,其他考虑因素可能包括您是否需要在图表上具有向下钻取功能。有这么多数据点,我无法想象你会这样做。


    如果你能回答以下问题,它可能会帮助人们制定更好的答案。

    1. 什么样的用户界面将呈现这些项目的输出? (例如 Excel、ASP.NET Web 应用程序、Windows 窗体、WPF、Silverlight 等。)

    2. 这些图表是应用户请求实时生成还是生成并存储?如果它们是按需生成的,您的用户认为可以等待的最长时间是多少?

    3. 实际使用 Excel 有多重要?您使用它是因为它是显示的要求,还是只是为了方便?

    4. “令人惊叹的因素”对于图表的显示有多重要?只是有图表,还是必须非常漂亮?

    5. 用户是否需要深入了解图表的能力,还是仅仅能够查看图像就足够了?

    【讨论】:

    • 感谢您的意见,安东尼。在乔恩给出建议之前,我已经快要考虑其他选择了。仅供参考,我开始使用 Excel 是因为 1)我的“围墙花园”用户群很容易使用它,并且 2)有时需要在自动生成后手动调整图表,并且用户都接受过 Excel 培训。跨度>
    【解决方案3】:

    为了帮助将来遇到此问题的任何人,以下是 Jon 修复的完整功能:

        public void DrawScatterGraph(string xColumnLetter, string yColumnLetterStart, string yColumnLetterStop, string xAxisLabel, string yAxisLabel, string chartTitle, Microsoft.Office.Interop.Excel.XlChartType chartType, bool includeTrendline, bool includeLegend)
        {
            int totalRows = dataSheet.UsedRange.Rows.Count; //dataSheet is a private class variable that 
                                                            //is already properly set to the worksheet
                                                            //we want to graph from
    
            if (totalRows < 2) throw new Exception("Not generating graph for " + chartTitle.Replace('\n', ' ') 
                                                   + " because not enough data was present");
    
            dataSheet.get_Range("Z1", "Z2").Select();   //we need to select some empty space
                                                        //so Excel doesn't try to jam the 
                                                        //potentially large data set into the 
                                                        //chart automatically
    
            ChartObjects charts = (ChartObjects)dataSheet.ChartObjects(Type.Missing);
            ChartObject chartObj = charts.Add(100, 300, 500, 300);
            Chart chart = chartObj.Chart;
            chart.ChartType = chartType;
            SeriesCollection seriesCollection = (SeriesCollection)chart.SeriesCollection(Type.Missing);
    
            if (totalRows < SizeOfSeries) //we can graph the data in a single series - yay!
            {
                Range xValues = dataSheet.get_Range(xColumnLetter + "2", xColumnLetter + totalRows.ToString());
                Range yValues = dataSheet.get_Range(yColumnLetterStart + "1", yColumnLetterStop + totalRows.ToString());
                chart.SetSourceData(yValues, XlRowCol.xlColumns);
    
                foreach (Series s in seriesCollection)
                {
                    s.XValues = xValues;
                }
            }
            else // we need to split the data across multiple series 
            {
                int startRow = 2; 
    
                while (startRow < totalRows)
                {
                    int stopRow = (startRow + SizeOfSeries)-1;  
                    if (stopRow > totalRows) stopRow = totalRows;
    
                    Series s = seriesCollection.NewSeries();
                    s.Name = "ChunkStartingAt" + startRow.ToString();
                    s.XValues = dataSheet.get_Range(xColumnLetter + startRow.ToString(), xColumnLetter + stopRow.ToString());
                    s.Values = dataSheet.get_Range(yColumnLetterStart + startRow.ToString(), yColumnLetterStop + stopRow.ToString());
    
                    startRow = stopRow+1;
                }
            }
    
            chart.HasLegend = includeLegend;
            chart.HasTitle = true;
            chart.ChartTitle.Text = chartTitle;
    
            Axis axis;
            axis = (Axis)chart.Axes(XlAxisType.xlCategory, XlAxisGroup.xlPrimary);
            axis.HasTitle = true;
            axis.AxisTitle.Text = xAxisLabel;
            axis.HasMajorGridlines = false;
            axis.HasMinorGridlines = false;
    
            axis = (Axis)chart.Axes(XlAxisType.xlValue, XlAxisGroup.xlPrimary);
            axis.HasTitle = true;
            axis.AxisTitle.Text = yAxisLabel;
            axis.HasMajorGridlines = true;
            axis.HasMinorGridlines = false;
    
            if (includeTrendline)
            {
                Trendlines t = (Trendlines)((Series)chart.SeriesCollection(1)).Trendlines(Type.Missing);
                t.Add(XlTrendlineType.xlLinear, Type.Missing, Type.Missing, 0, 0, Type.Missing, false, false, "AutoTrendlineByChameleon");
            }
    
            chart.Location(XlChartLocation.xlLocationAsNewSheet, "Graph");
        }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-08-19
      • 1970-01-01
      • 2013-10-23
      • 1970-01-01
      • 2014-09-04
      • 2012-06-19
      • 2017-11-16
      相关资源
      最近更新 更多