C#/ Excel:在图表上处理最大系列大小

我需要编程方式帮助绘制比单个Excel系列更多的点。

根据http://office.microsoft.com/en-us/excel/HP100738491033.aspx在Excel 2007图表上可显示的最大点数是256000.考虑到每个系列在32000点上限,需要8个系列绘制完整的256000点。 由于我们使用的大型数据集,我的客户需要绘制每个图表的最大点数。

我对C#/ Excel interop有适度的经验,所以我认为可以很容易地编程创build一个工作表,然后循环遍历每一组32000点,并将它们作为一个系列添加到图中,当数据完全绘制时停止或8个系列被绘制。 如果着色正确,8系列将在一个系列视觉上难以区分。

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

(全尺寸) 在二维图表的数据系列中,您可以使用的最大数据点数量为32,000 … http://img14.imageshack.us/img14/9630/errormessagen.png

奇怪的是,这个popup窗口出现在执行该行时:

chart.ChartType = chartType(其中chartType是xlXYScatterLines)http://img2.imageshack.us/img2/2413/linean.png

并伴随着:

来自HRESULT的exception:0x800AC472 http://img21.imageshack.us/img21/5153/exceptionb.png

我甚至不明白在我甚至指定要绘制数据之前,我怎么会产生这样的popup/警告/exception。 Excel试图在这里很聪明吗?

作为一个临时解决方法,我已经把chart.ChartType = chartType语句放到try-catch块中,这样我就可以继续下去了。

如下所示,我的“块”代码正在按预期工作,但在将数据添加到graphics时仍然遇到同样的问题。 Excel表示,我试图绘制太多点,当我清楚我不是。

( 全尺寸图片 ) 带监视窗口的代码块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"); } 

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

select不在数据旁边的空白单元格,然后插入图表。 这将是空白的,而不是预先填充。

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

一个build议可能是使用第三方组件来生成图。 如何做到这一点的具体技术取决于你是否必须能够在Excel中查看数据或输出graphics是否需要在别处可用。

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

如果您确实需要使用excel查看graphics,则可以调用外部graphics应用程序并将其传递给一组数据点。 当它返回的图像插入它与Excel中的Excel。

如果需要的话,我可以给你两种方法的更多信息。

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


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

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

  2. 这些图应该是根据用户的要求实时生成的,还是生成并存储的? 如果它们是按需生成的,那么用户可以接受的等待时间的最长时间是多less?

  3. 你真的使用Excel有多重要? 您是否使用它,因为这是显示的要求,或者只是方便吗?

  4. 显示图表的“哇因素”有多重要? 是简单地有图表,还是他们必须非常漂亮?

  5. 用户是否需要任何能力深入图表,或者只是能够查看图像足够?

为了帮助将来遇到这个问题的人,Jon的解决方法是完整的function:

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