6

I need to change the source data in a PowerPoint chart using VSTO. The first code snippet shows how I get the series from my shape object.

SeriesCollection seriesCollection = shape.Chart.SeriesCollection();
Series series = seriesCollection.Item(1);

I've tested that it works by calling

series.Delete()

Which will remove the series from the chart. However, if possible, I would like to just change the values of the series instead. I've tried the following:

series.Values = myArray1;
series.XValues = myArray2;

Which results in an empty graph. (The Series names and title remain, but the data is missing).

Does anyone know how this can be accomplished? My current solution is to regenerate the chart in excel and replace the one in PowerPoint, but I was hoping there is a more efficient way.

EylM
  • 5,967
  • 2
  • 16
  • 28
User892313
  • 225
  • 3
  • 19

1 Answers1

5

Powerpoint chart is based on underlying Excel worksheet with chart data stored in the cells. If you invoke context menu for the chart and click "Edit Data...", Excel application will be launched with worksheet containing values from the chart.

Therefore you should modify Powerpoint chart not by setting series values directly. You should do it by updating cells values in underlying Excel worksheet. Here is a working sample:

static void Main(string[] args)
{
    var powerPointApp = new Microsoft.Office.Interop.PowerPoint.Application();
    var presentation = powerPointApp.Presentations.Open(@"Presentation1.pptx");
    var slide = presentation.Slides[1];
    var shape = slide.Shapes[1];

    var chartData = shape.Chart.ChartData;
    chartData.Activate();

    var workbook = chartData.Workbook;
    workbook.Application.Visible = false;
    var dataSheet = workbook.Worksheets[1];

    double[] newData = {1, 2, 3, 4, 5};

    var colNumber = 2;
    var firstRowNumber = 2;

    //  Clearing previous data
    dataSheet.UsedRange.Columns[colNumber, Type.Missing].Clear();

    for (var i = 0; i < newData.Length; ++i)
    {
        dataSheet.Cells[firstRowNumber + i, colNumber] = newData[i];
    }

    //  Saving and closing Excel workbook before presentation could be saved.
    workbook.Close(true);
    presentation.Save();
    presentation.Close();
    powerPointApp.Quit();
}

In code above there are two hardcoded values:

var colNumber = 2;
var firstRowNumber = 2;

Chart underlying worksheet has a predefined structure. For example for default chart it builds following workbook:

enter image description here

So you could rely on following assumptions when modifying the data in worksheet:

  • The first row for the values series is equal to 2.
  • The column corresponding to specific series is equal to 1 + series index. Series index here is the same index that you pass to SeriesCollection.Item(object Index) call to get the series (starting from 1). So 2nd worksheet column correspond to the 1st chart series and so on.

UPDATE (For problem with Waterfall chart)

The problem with Waterfall chart also reproduces in my environment. I have checked all other 15 chart types and the same error happens for following of them:

  • Treemap
  • Sunburst
  • Histogram
  • Box & Whisker
  • Waterfall

The root cause of the problem is that chartData.Activate() call does not actually open worksheet associated with the chart. The problem is specific to Interop because the worksheet is opened successfully if you select "Edit data..." in PowerPoint application.

All these charts were added in Office 2016. Everything works fine for charts that were available in previous versions of the Office. Seems like the issue is caused by problem of Interop assemblies intergration with 2016 office. There are still no Primary Interop Assemblies for Office 2016, it still ships with PIA from Office 2013. Unfortunately, I can't propose a fix or workaround for this specific problem. We have to wait for the fix from Microsoft team.

CodeFuller
  • 30,317
  • 3
  • 63
  • 79
  • What is `chartData.Activate()` for? – Daniel Hilgarth Mar 23 '18 at 16:37
  • It opens worksheet associated with the chart. Without activation, below call to `ChartData.Workbook` will fail. – CodeFuller Mar 23 '18 at 16:45
  • So it will be visible to the end user? I just tested it. It worked without `Activate` on a line chart. It was the currently selected chart however. Maybe this makes a difference? – Daniel Hilgarth Mar 23 '18 at 16:47
  • You could hide Excel application by setting `workbook.Application.Visible` to `false`. I've just added it to my answer. However Excel window will anyway appear for a moment before it will be hidden. [This question](https://stackoverflow.com/questions/43779508/) addresses this problem, but unfortunately it does not have an answer. I'm not sure how the code works in your case without activation. It fails for me even if I select the chart. If chart is successfully updated without `Activate()`, I believe you could do update without this call. – CodeFuller Mar 23 '18 at 17:03
  • Strange. And what about a waterfall chart (chart type 119)? This code doesn't work because `Workbook` throws even when calling `Activate()` – Daniel Hilgarth Mar 23 '18 at 17:07
  • Could you please share presentation file with such waterfall chart. I can't find it in my chart templates. – CodeFuller Mar 23 '18 at 17:22
  • I think this is a new type in Office 2016: https://support.office.com/en-us/article/create-a-waterfall-chart-in-office-2016-8de1ece4-ff21-4d37-acd7-546f5527f185 – Daniel Hilgarth Mar 23 '18 at 17:23
  • @DanielHilgarth, I have checked the code with Office 2016. For me reference to `Workbook` also succeeds without `chartData.Activate()` call. However then exception is thrown within Excel application. I believe it's a must to call `Activate()` before using workbook, it's also stated in [documentation](https://msdn.microsoft.com/en-us/library/microsoft.office.interop.powerpoint.chartdata.activate%28v=office.14%29.aspx): "You must call this method before referencing the Workbook property". Regarding Waterfall chart - see update of my answer. – CodeFuller Mar 24 '18 at 07:54
  • Great, thanks for the effort. It's a real pity that MS is so behind here – Daniel Hilgarth Mar 24 '18 at 07:55
  • A better clear call is "dataSheet.UsedRange.Clear();" With that said, awesome post - very helpful! – David Thielen Dec 06 '18 at 15:39