[Skip to Content]

Writing MS Office Documents from Silverlight 3

A few people have mentioned that Silverlight 3 still does not have print screen functionality. Here at Dot Net Solutions we don’t mourn this missing feature – no we revel in the opportunity.

Silverlight 3 does now have a save file feature and this enables you to create far better solutions: You can create MS Office documents on the client – either from scratch or from a template.

MS Office documents (.docx, .pptx, .xslx) are zip files with xml (document structure, text etc.) and binary (embedded images etc.) files. As long as you have a way to write files, and a library for managing zip files you can write MS Office file.

A convenient way to write zip files from Silverlight is with a 3rd party library - SharpZipLib provides Zip writing capabilities and is 100% C#. I’ve made some minor changes to convert this to Silverlight:

  • Removed classes such as GZip, Tar etc. to reduce the size as they are unneeded for this example.
  • Converted from ArrayList etc. to generic List<> and Dictionary<>.
  • Changed default code page from ansi to utf-8

The previous hard work was by Mike Krueger and John Reilly – any new errors are mine. The GNU general public licence remains in place. You can obtain source code for the the modified library with the demo files below.

The main page of the Silverlight application has a button that calls a save file method

private void SaveFile_Click(object sender, RoutedEventArgs e)
{
    // create the save file dialog and setup extensions allowed SaveFileDialog dialog = new SaveFileDialog();
    dialog.Filter = "PowerPoint Files(*.pptx) | *.pptx";
    dialog.DefaultExt = "*.pptx";

    // show the save file dialog bool? doSave = dialog.ShowDialog();

    // check that the dialog returned a value, and that the value is true. if (doSave.HasValue && doSave.Value)
    {
        // open the stream provided using (Stream file = dialog.OpenFile())
        {
            GeneratePowerPointFile(file);
        }
    }
}

If you’re stepping through this in a debugger, you’ll probably get an error on the line

using (Stream file = dialog.OpenFile())

Silverlight applies several checks that the save file dialog is being prompted directly from user interaction – the debugger interferes with these, so if you must, just go straight to the GeneratePowerPointFile method.

private void GeneratePowerPointFile(Stream file)
{
    /// open the output file as a ZipOutputStream. 
/// This allows writing to a stream into which are embedded the
/// various zip file entries.
using (ZipOutputStream outputFile = new ZipOutputStream(file)) { // Open the embedded pptx template in a memory stream using (MemoryStream templateBuffer =
new MemoryStream(TemplateResources.Template)) { /// Open the memory stream as a ZipInputStream. /// This allows reading the files embedded in the zip file using (ZipInputStream templateFile = new ZipInputStream(templateBuffer)) { ZipEntry entry; /// iterate over each of the files embedded in the template zip file while ((entry = templateFile.GetNextEntry()) != null) { // clone the useful information on the entry ZipEntry newEntry = new ZipEntry(entry.Name); // write the entry to the zip file creating a file to be // extracted later outputFile.PutNextEntry(newEntry); /// for demo, just modify one content item. if (String.Equals( entry.Name, "ppt/slides/slide1.xml", StringComparison.OrdinalIgnoreCase)) { WriteModifiedSlide(templateFile, outputFile, entry); } /// write everything else directly to the output zip file else { CopyStreamToOutputFile(templateFile, outputFile); } } templateFile.Close(); } outputFile.Close(); } } }

Nothing complicated here – the demo app has a template powerpoint document stored in TemplateResources.Template, this is opened as a stream, then opened as a ZipInputStream to read each of the entries. Likewise a ZipOutputStream is created to handle writing the new .pptx file.

There are several resources available on writing Open XML files already, so I’m not doing anything too complicated here – just using Linq to Xml to modify some of the content in an existing file identified with the string match.

private static void WriteModifiedSlide(ZipInputStream templateFile, 
    ZipOutputStream outputFile, ZipEntry entry)
{
    byte[] buffer = new byte[entry.Size];
    templateFile.Read(buffer, 0, (int)entry.Size);
    XDocument slide = XDocument.Load(new MemoryStream(buffer));
    var textRows = slide
        .Descendants(nameT)
        .ToList();
    textRows[0].Value = "My Slide";
    textRows[1].Value = "Hello World";
    var streamToWrite = GetStream(slide);
    outputFile.Write(streamToWrite, 0, streamToWrite.Length);
}

Whilst this is relatively simple, it shows the basis of a technique we’ve recently applied to writing MS Excel and MS Word files and can be extended to producing reports, financial statements etc.

Sample Project

Additional Resources:

Prior art:

  • Rob Houweling was first to convert the SharpZipLib to Silverlight and has a complete version on CodePlex

Corrections

Comments

comments powered by Disqus