Tag-Archive for ◊ OpenXml ◊

Author:
Tuesday, February 02nd, 2010

Anyone who has tried their hand at OpenXml programming would concur that its a bit daunting at start with. Anyone tried their hands at making charts in OpenXml would agree even more. There are, of course, several code snippets on the internet to help you out. However, I didn’t find any that would help me with simple charts in PowerPoint. And the one I found for Excel, for instance, was too complicated to be used and involved creating a chart from the scratch.

All I wanted was a simple way to put my data into in existing bar chart in a powerpoint slide. That way I can give the user the freedom to have any formatting options that he wishes for the chart and only the data will be filled in by my program.

You’ll see the code snippet below. Basically I delete the existing “BarChartSeries” and insert my own with my data. I don’t touch any other part of the Chart leaving the formatting intact.

It may not work for all your scenarios but its a simple thing to start with. Note that I’m using Microsoft’s OpenXml SDK.

private void button1_Click(object sender, EventArgs e)
{
Dictionary<string, int> values = new Dictionary<string, int>();
values.Add("hello", 20);
values.Add("world", 30);
values.Add("test", 40);
values.Add("data", 50);

PresentationDocument presentationDoc = PresentationDocument.Open("c:\\p2.pptx", true);
SlidePart slidePart = presentationDoc.PresentationPart.SlideParts.First();
ChartPart chartPart = slidePart.ChartParts.First();

ReplaceValuesInChartInSlide(chartPart, values, "my category");
presentationDoc.PresentationPart.Presentation.Save();
presentationDoc.Close();
}

public static void ReplaceValuesInChartInSlide(ChartPart chartPart, Dictionary<string, int> data, string categoryTitle)
{
ChartSpace chartSpace = chartPart.ChartSpace;
List<int> indexOfUsedItems = new List<int>();

BarChart barChart = chartSpace.Descendants<BarChart>().FirstOrDefault();

barChart.RemoveAllChildren<BarChartSeries>();
uint i = 0;
foreach (string key in data.Keys)
{
BarChartSeries barChartSeries = barChart.AppendChild<BarChartSeries>(new BarChartSeries(new Index() { Val = new UInt32Value(i) },
new Order() { Val = new UInt32Value(i) },
new SeriesText(new NumericValue() { Text = key })));

StringLiteral strLit = barChartSeries.AppendChild<CategoryAxisData>(new CategoryAxisData()).AppendChild<StringLiteral>(new StringLiteral());
strLit.Append(new PointCount() { Val = new UInt32Value(1U) });
strLit.AppendChild<StringPoint>(new StringPoint() { Index = new UInt32Value(0U) }).Append(new NumericValue(categoryTitle));

NumberLiteral numLit = barChartSeries.AppendChild<DocumentFormat.OpenXml.Drawing.Charts.Values>(
new DocumentFormat.OpenXml.Drawing.Charts.Values()).AppendChild<NumberLiteral>(new NumberLiteral());
numLit.Append(new FormatCode("General"));
numLit.Append(new PointCount() { Val = new UInt32Value(1U) });
numLit.AppendChild<NumericPoint>(new NumericPoint() { Index = new UInt32Value(0u) }).Append(new NumericValue(data[key].ToString()));

i++;
}

chartSpace.Save();
}

The p2.pptx in the code simply contained a slide with a barchart.

Note that a chart in powerpoint has its associated data in an embedded excel file. In this sample we haven’t touched that excel file and hence haven’t changed that data. Hence, although your chart will render new data correctly, it may not show the new data in that excel (it appears when click “Edit Data” for the chart in PowerPoint). Anyways, this simple code worked for us cos our primary aim was to render the graph.

Author:
Tuesday, February 02nd, 2010

This document explains, as to how one can write data into an excel file using openxml.

Here we are considering that we already have an excel file and we are only writing data into the file and also assuming that the data which has to be written into excel is stored in a datatable.

To make use of openxml we first need to add reference to the openxml dll and use the following namespaces.

using DocumentFormat.OpenXml.Packaging;

using DocumentFormat.OpenXml.Spreadsheet;

using DocumentFormat.OpenXml;

 

To write the data, we first need to open the excel file. The below mentioned code is used to perform the same.

    public static void OpenExcelDocument(DataSet ds, string xlsxFilePath)

    {

        using (SpreadsheetDocument myWorkbook = SpreadsheetDocument.Open(xlsxFilePath, true))

        {

 

            //Access the main Workbook part, which contains all references

            WorkbookPart workbookPart = myWorkbook.WorkbookPart;

 

            foreach (DataTable dt  in ds.Tables)

            {

                InsertValuesInSheets(dt.TableName, workbookPart, dt);

            }

 

            workbookPart.Workbook.Save();

          

        }

    }

This method accepts two parameters, one is the dataset and the other is the complete path of the excel file. We are using a dataset here as an excel file can contain n number  of worksheets inside it, so the data that has to be added in each sheet can be stored in a datatable, which is intern added to the dataset. In the above method we are calling the InsertvaluesInsheets method for each table in the dataset.

Now let’s see what this InsertValuesInSheet method does. It accepts three parameters, the sheetname in excel, the workbookpart and the datatable which contains the data to be written. We first need to get the worksheetpart where the data has to be inserted, we use the sheetname as show below to get that particular worksheet part.

Sheet ss = workbookPart.Workbook.Descendants<Sheet>().Where(s=>s.Name== sheetName).SingleOrDefault<Sheet>();

 WorksheetPart           worksheetPart = (WorksheetPart)workbookPart.GetPartById(ss.Id);

 

If incase we have single sheet in the excel file then we do not need the sheetname and instead we can use the below line to get the first worksheet part from excel.

WorksheetPart worksheetPart = workbookPart.WorksheetParts.FirstOrDefault();

Next we get the sheet data and get the rows in the sheet by using the following line of code.

var excelRows = sheetData.Descendants<Row>().ToList();

 

Then we loop through each row in the datatable and check if the row exists in the excel sheet and if it exists, then we get the cells in that row and if cell exists, we update the cell value with the corresponding values from the datatable else we create a new cell, update the cell value and then add the cell to the excel row. Incase the row does not exists then we create a new row and add the corresponding values from the datatable and then added this to the excel sheet. 

Once all the data is written into the excel sheet then we save the changes that we made by calling the following line of code.

worksheetPart.Worksheet.Save();

public static void InsertValuesInSheets(string sheetName,WorkbookPart workbookPart,DataTable dt)

    {

        WorksheetPart worksheetPart = null;

        if (!string.IsNullOrEmpty(sheetName))

        {

            Sheet ss = workbookPart.Workbook.Descendants<Sheet>().Where(s => s.Name == sheetName).SingleOrDefault<Sheet>();

            worksheetPart = (WorksheetPart)workbookPart.GetPartById(ss.Id);

        }

        else

        {

            worksheetPart = workbookPart.WorksheetParts.FirstOrDefault();

        }

     

        if (worksheetPart != null)

        {

            SheetData sheetData = worksheetPart.Worksheet.GetFirstChild<SheetData>();

            var excelRows = sheetData.Descendants<Row>().ToList();

    int excelRowIndex = 1;

                for (int rowIndexInTable = 0; rowIndexInTable < dt.Rows.Count; rowIndexInTable++)

                {

                    if (excelRowIndex >= excelRows.Count)

                    {

                        Row contentRow = CreateContentRow(excelRowIndex+1, dt.Rows[rowIndexInTable], dt.Columns.Count);

                        sheetData.AppendChild(contentRow);

                    }

                    else

                    {

                        var cells = excelRows[excelRowIndex].Descendants<Cell>().ToList();

                        for (int colIndexInTable = 0; colIndexInTable < dt.Columns.Count; colIndexInTable++)

                        {

                            Cell cell;

                            if (colIndexInTable >= cells.Count)

                            {

                               cell = new Cell();

                                    excelRows[excelRowIndex].AppendChild(AddValueToCell(cell, dt.Rows[rowIndexInTable][colIndexInTable].ToString()));

                            }

                            else

                            {

                                if (cells.Count == 1)

                                {

                                    cell = new Cell();

                                    excelRows[excelRowIndex].InsertAt(AddValueToCell(cell, dt.Rows[rowIndexInTable][colIndexInTable].ToString()), 0);

                                }

                                else

                                {

                                    cell = cells[colIndexInTable];

                                    AddValueToCell(cell, dt.Rows[rowIndexInTable][colIndexInTable].ToString());

                                }

                            }

 

                        }

                    }

                    excelRowIndex++;

                }

            worksheetPart.Worksheet.Save();

        }

    }

To create a new row and to add the same to the excel sheet we make use of another method called, CreateContentRow as shown below.

In this method we create a new row assign the index of the row and loop through the no.of columns in datatable and create new cell and added it to the row. To create a new cell we use the CreateTextCell method.

public static Row CreateContentRow(int index, DataRow dr, int headerColumnsCount)

    {

 

        //Create new row

 

        Row r = new Row();

 

        r.RowIndex = (UInt32)index;

 

        for (int i = 0; i < headerColumnsCount; i++)

        {

 

            Cell c = CreateTextCell(headerColumns[i], dr[i].ToString(), index);

            r.AppendChild(c);

 

        }

        return r;

    }

 

public static Cell CreateTextCell(string header, string text, int index)

    {

 

        //Create new inline string cell

 

        Cell c = new Cell();

 

        c.DataType = CellValues.InlineString;

 

        //Add text to text cell

 

        InlineString inlineString = new InlineString();

 

        Text t = new Text();

 

        t.Text = text;

 

        inlineString.AppendChild(t);

 

        c.AppendChild(inlineString);

 

        return c;

 

    }

}

Incase the cell is already existing in the excelsheet and we just have to update the data then we use the following method.

public static Cell AddValueToCell(Cell cell, string text)

    {

        cell.DataType = CellValues.InlineString;

        cell.RemoveAllChildren();

        InlineString inlineString = new InlineString();

        Text t = new Text();

        t.Text = text;

        inlineString.AppendChild(t);

        cell.AppendChild(inlineString);

        return cell;

    }

By using all the above methods we can write data into excel file using openxml.

Category: .Net | Tags:  | 19 Comments
Author:
Thursday, October 01st, 2009

With Office 2007, Microsoft introduced a new document format called OpenXml. Its an xml based format for storing Mirosoft office documents. Technically, an Office 2007 file (pptx, docx etc) is simply a zip file containing a set of xml files and related content. You can actually, rename it to .zip and extract its contents! Although the idea is novel, the xml format itself is a little complex to work with. Microsoft does provide a OpenXML SDK to make life a little easier for programmers.

However, you’ll still need to learn a lot of tricks to get things done in OpenXml. I’ll demonstrate one such common task here. If you’re working with an OpenXml PowerPoint (pptx) file, one of the first things you may want to learn is how to clone a slide. i.e. make a copy of a slide along with all its contents. There are several sites that’ll give you code to Clone a slide. Here’s a good one which I’m using as a reference for my code: http://blogs.msdn.com/brian_jones/archive/2009/08/13/adding-repeating-data-to-powerpoint.aspx

However, I didn’t find any site that has a code which works with charts. When you clone a slide, all its parts should also be cloned for it to work properly. For eg. images are stored in a property called ImagePart. If you want images from source slide to appear in the cloned slide, you should copy the ImagePart too.

Similary charts are stored in a ChartPart. However, charts are more tricky as they refer to an embedded excel sheet that contains chart data. You’ll need to copy this excel sheet too. The following function has the sample code for charts and the embedded excel. It also has code to clone images to help you compare the differences:

public static SlidePart CloneSlidePartWithImagesAndCharts(PresentationPart presentationPart, SlidePart slideTemplate)
{
int i = presentationPart.SlideParts.Count();
//Create a new slide part in the presentation.
SlidePart newSlidePart = presentationPart.AddNewPart<SlidePart>(“newSlide” + i);
i++;
//Add the source slide content into the new slide.
newSlidePart.FeedData(slideTemplate.GetStream(FileMode.Open));
//Make sure the new slide references the proper slide layout.
newSlidePart.AddPart(slideTemplate.SlideLayoutPart, slideTemplate.GetIdOfPart(slideTemplate.SlideLayoutPart));

// copy the image parts
foreach (ImagePart ipart in slideTemplate.ImageParts)
{
ImagePart newipart = newSlidePart.AddImagePart(ipart.ContentType, slideTemplate.GetIdOfPart(ipart));
newipart.FeedData(ipart.GetStream());
}

// copy the chart parts
foreach (ChartPart cpart in slideTemplate.ChartParts)
{
ChartPart newcpart = newSlidePart.AddNewPart<ChartPart>(slideTemplate.GetIdOfPart(cpart));
newcpart.FeedData(cpart.GetStream());
// copy the embedded excel file
EmbeddedPackagePart epart = newcpart.AddEmbeddedPackagePart(cpart.EmbeddedPackagePart.ContentType);
epart.FeedData(cpart.EmbeddedPackagePart.GetStream());
// link the excel to the chart
(((newcpart.ChartSpace)).ExternalData).Id = newcpart.GetIdOfPart(epart);
newcpart.ChartSpace.Save();
}

//Get the list of slide ids.
SlideIdList slideIdList = presentationPart.Presentation.SlideIdList;

//Deternmine where to add the next slide (find max number of slides).
uint maxSlideId = 1;
SlideId prevSlideId = null;
foreach (SlideId slideId in slideIdList.ChildElements)
{
if (slideId.Id > maxSlideId)
{
maxSlideId = slideId.Id;
prevSlideId = slideId;
}
}
maxSlideId++;
//Add the new slide at the end of the deck.
SlideId newSlideId = slideIdList.InsertAfter(new SlideId(), prevSlideId);
//Make sure the id and relid are set appropriately.
newSlideId.Id = maxSlideId;
newSlideId.RelationshipId = presentationPart.GetIdOfPart(newSlidePart);
newSlidePart.Slide.Save();
return newSlidePart;
}

Category: .Net | Tags: , , , , , ,  | 7 Comments