简体   繁体   English

C#/ Excel:解决图表上的最大序列大小

[英]C#/Excel: Working Around Maximum Series Size On Chart

I need help programatically graphing more points than can fit in a single Excel series. 我需要以编程方式绘制比单个Excel系列中更多的点的帮助。

According to http://office.microsoft.com/en-us/excel/HP100738491033.aspx the maximum number of points displayable on an Excel 2007 chart is 256000. Given that each series caps out at 32000 points, 8 series are required to plot the full 256000 points. 根据http://office.microsoft.com/zh-cn/excel/HP100738491033.aspx,Excel 2007图表上可显示的最大点数为256000。鉴于每个系列的上限为32000点,因此需要8个系列绘制全部256000点。 My customer requires plotting of maximum amount of points per chart due to the large data sets we work with. 由于我们要处理大量数据,因此我的客户需要绘制每个图表的最大点数。

I have moderate experience with C#/Excel interop so I thought it would be easy to programatically create a worksheet and then loop through each set of 32000 points and add them to the graph as a series, stopping when the data was fully plotted or 8 series were plotted. 我对C#/ Excel互操作有一定的经验,因此我认为以编程方式创建工作表,然后遍历每组32000点并将它们作为一个序列添加到图形中会很容易,在完全绘制数据或8个序列时停止被绘制。 If colored properly, the 8 series would be visually indistinguishable from a single series. 如果上色正确,则8系列与单个系列在视觉上无法区分。

Unfortunately here I am. 不幸的是我在这里。 The main problem I encounter is: 我遇到的主要问题是:

(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 (完整大小) 您可以在一个数据系列中用于二维图表的最大数据点数是32,000 ... http://img14.imageshack.us/img14/9630/errormessagen.png

This pop-up, strangely enough, appears when I execute the line: 奇怪的是,当我执行该行时会出现以下弹出窗口:

chart.ChartType = chartType(其中chartType为xlXYScatterLines)

and is accompanied by: 并附有:

Exception from HRESULT: 0x800AC472 http://img21.imageshack.us/img21/5153/exceptionb.png 来自HRESULT的异常:0x800AC472 http://img21.imageshack.us/img21/5153/exceptionb.png

I do not understand how I could be generating such a popup/warning/exception before I have even specified the data to be graphed. 我什至在指定要绘制图形的数据之前都不了解如何生成这样的弹出窗口/警告/异常。 Is Excel trying to be clever here? Excel试图在这里变得聪明吗?

As a temporary workaround, I've put the chart.ChartType = chartType statement into a try-catch block so I can keep going. 作为临时的解决方法,我将chart.ChartType = chartType语句放入try-catch块中,以便继续进行下去。

As the following shows, my "chunking" code is working as intended, but I still encounter the same problem when trying to add data to the graph. 如下所示,我的“块”代码按预期工作,但是在尝试向图中添加数据时仍然遇到相同的问题。 Excel says I am trying to graph too many points when clearly I am not. Excel说我明显不在试图绘制太多点的图形。

( full size image ) code block with watch window http://img12.imageshack.us/img12/5360/snippet.png 带有 完整尺寸的图片带有观看窗口的代码块http://img12.imageshack.us/img12/5360/snippet.png

I understand I may not have the X Values correctly associated with each series yet, but I'm trying to get this to work before I go further. 我知道我可能还没有将X值正确地与每个系列相关联,但是我正在尝试使其更进一步。

Any help would be greatly appreciated. 任何帮助将不胜感激。

Here's the full code: 这是完整的代码:

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

Does your graph actually have to be in Excel? 您的图形实际上必须在Excel中吗? With that many data points the performance would be horrible. 拥有如此多的数据点,性能将非常糟糕。

One suggestion might be to use a third party component to generate the graph. 一个建议可能是使用第三方组件来生成图形。 The specific technique for how to accomplish this depends on whether you have to be able to view the data in excel or whether the output graph simply needs to be available elsewhere. 如何完成此操作的具体技术取决于您是否必须能够在excel中查看数据,或者输出图是否仅需要在其他地方使用。

If the graph does not need to be visible within Excel, then just pass the data points and view the image in the graphing application or a web browser. 如果在Excel中不需要显示图形,则只需传递数据点并在图形应用程序或Web浏览器中查看图像即可。

If you do need to view the graph with excel, you could make a call to the external graphing application and pass it a collection of data points. 如果确实需要使用excel查看图形,则可以调用外部图形应用程序并将其传递给数据点的集合。 When it returns the image just insert it in excel with vba. 当它返回图像时,只需使用vba将其插入excel。

I can give you more info on both approaches if you need. 如果需要,我可以为您提供有关这两种方法的更多信息。

Also, other considerations might include whether you need to have drill down capability on the graph. 另外,其他注意事项可能包括是否需要在图形上具有向下钻取功能。 With this many data points, I can not imagine that you would. 有了这么多数据点,我无法想象您会那样。


If you can answer the following questions, it might help folks formulate better answers. 如果您可以回答以下问题,则可能会帮助人们制定更好的答案。

  1. What sort of user interface will be presenting the output of these items? 什么样的用户界面将显示这些项目的输出? (eg Excel, ASP.NET Web Application, Windows Forms, WPF, Silverlight, other.) (例如,Excel,ASP.NET Web应用程序,Windows窗体,WPF,Silverlight等)。

  2. Are these graphs supposed to be generated in real time at a user's request or are they generated and stored? 这些图是应该根据用户的要求实时生成还是要生成和存储? If they are generated on demand, what is the maximum amount of time your users would consider acceptable to wait? 如果它们是按需生成的,那么您的用户认为可以等待的最长时间是多少?

  3. How important is it that you actually use Excel? 实际使用Excel有多重要? Are you using it because it is a requirement for display, or is that just what is handy? 您使用它是因为它是显示的必要条件,还是方便使用?

  4. How important is the "Wow factor" for the display of the graphs? “哇系数”对于图形显示有多重要? Is simply having the graphs, or do they have to be extremely beautiful? 只是拥有图形,还是必须非常漂亮?

  5. Do users require any ability to drill down into the graph, or is simply being able to view the image sufficient? 用户是否需要任何能力来深入研究图形,或者仅仅是能够查看图像就足够了?

If the active cell is in a block of data, Excel may assume you want to plot the range. 如果活动单元格在数据块中,Excel可能会假定您要绘制范围。

Select a blank cell which is not next to the data, then insert the chart. 选择一个不在数据旁边的空白单元格,然后插入图表。 It will be blank, rather than prepopulated. 它将为空白,而不是预先填充。

To help anyone who comes across this in the future, here's the complete function with Jon's fix: 为了帮助将来遇到此问题的任何人,这是乔恩修复程序的完整功能:

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

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

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