How to Create PowerPoint Slides From Silverlight


Introduction

In this article, I will explain how a Silverlight application can be used to create slides in Microsoft PowerPoint file by taking various image captures of Silverlight pages. I was motivated to write this because I have not seen a solution in internet when I wanted to create a PowerPoint slides from a Silverlight project, but many results on using Silverlight in PowerPoint.

How it works

The solution has three projects.

  1. PowerpointService (a WCF Service project)
  2. SilverlightToPowerpoint (a Silverlight project)
  3. SilverlightToPowerpoint.Web (a web application hosting the Silverlight project)

Silverlight project makes a call to WCF Service by sending image captures of pages (byte array as image) with a title. In return, WCF creates PowerPoint slides and sends back the file as a Byte array. Then Silverlight prompts the user for where to save the file using a Save dialog box to save the byte array as Microsoft PowerPoint.

The WCF Service becomes necessary because we want to manipulate a file which is not supported in Silverlight as it runs in the client's browser and accessing a file is denied. (I hear someone of you say "Hey! there is an Isolated Storage", but for this to work that is not an ideal way.)

Third party libraries:

Third party libraries are used to take an image capture of Silverlight and create slides in Microsoft PowerPoint.

For taking a snap of Silverlight the following libraries are used in the Silverlight project:
  1. ImageTools.dll
  2. ImageTools.IO.Png.dll
  3. ICSharpCode.SharpZipLib.Silverlight.dll

These are the free libraries that can be downloaded from Codeplex.

For creating slides, DocumentFormat.OpenXml.dll is used in the WCF Service project. This library can be downloaded from OpenXmlDeveloper.

Note: You really do not need to download these libraries as they are part of the solution attached here. The links are for your further reference only.

Screen shots

In the Silverlight project I have used three images of three cars, but these images are stored in the web project under ClientBin->Images folder to reduce the size of Silverlight project.

So, the first page when you run Silverlight is:


PwrSilver1.gif

After select a car:


PwrSilver2.gif

After adding to slide collection (by default the car name is shown as title, but this can be changed):


PwrSilver3.gif

Taking multiple cars (in the code I set the maximum as 20, but you can change it):


PwrSilver4.gif

Save the slides into a PowerPoint file:


PwrSilver5.png

Confirmation:


PwrSilver6.PNG

And the PowerPoint:


PwrSilver7.png

Code Walkthrough

Silverlight project

It has one user control file MainPage.xaml file where all the UI design is written. Though the .xaml is self-explanatory and easy to understand by any Silverlight developer, the below markup code requires a special explanation.
Collapse | Copy Code

<stackpanel name="spSlide" grid.row="1" />
           <img name="imgCar" stretch="UniformToFill" /></img />

<
/stackpanel /
>

This StackPanel is very important as in the code; I convert the StackPanel and its child control as image and pass it to WCF Service. Here I have just used an Image only, but if you want to add more controls you must add under this StackPanel. Of course, in the code I used the StackPanel, but you can give any other container control for that matter.

This is the code that is executed when you click the Add to Slide:

if (cbCars.SelectedValue == null) return; //No item is chosen 
            // comment this line if you want allow unlimited number of slides. or update the number
            if (_slides.Count > 20) throw new Exception("Maximum slides (20) added already!");
            //convert the stackpanel and its controls as image and store it in memory
            WriteableBitmap image = new WriteableBitmap(spSlide, new ScaleTransform());
            try
            {
                PptSlide slide = new PptSlide();
                byte[] binaryData = GetImageBufferAsPng(image);
                BitmapImage bmpImage = new BitmapImage();
                Stream stream = new MemoryStream(binaryData, 0, binaryData.Length);
                bmpImage.SetSource(stream);
                slide.Image = bmpImage;
                slide.ImageBinary = binaryData;
                slide.ImageTitle = string.Format("{0}_{1}", ((ComboBoxItem)cbCars.SelectedItem).Content, _slides.Count + 1);
                _slides.Add(slide);
                tabSlides.Header = string.Format("Slides ({0})", _slides.Count);
            }
            catch (Exception ex)
            {
                ShowErrorAlert(string.Format("Error while adding to slide collection:\n{0}", ex.Message));
            }

This code uses the image libraries to convert the StackPanel and its controls to an image of "png" and add it to the internal observable collection which is a source of the Datagrid.

The code gets executed when you click the Save as PowerPoint:

  if (_slides.Count == 0) return;
            try
            {
                var service = new PowerpointClient();
                var slides = new List<powerpointimage />();
                foreach (var item in _slides)
                {
                    var slide = new PowerpointImage();
                    slide.ByteArray = item.ImageBinary;
                    slide.Title = item.ImageTitle;
                    slides.Add(slide);
                }

                SaveFileDialog = new SaveFileDialog();
                SaveFileDialog.DefaultExt = "Powerpoint 2007 (*.pptx)|*.pptx";
                SaveFileDialog.Filter = "Powerpoint 2007 (*.pptx)|*.pptx";
                SaveFileDialog.FilterIndex = 1;
                if (SaveFileDialog.ShowDialog() == true)
                {
                    service.PowerpointAsByteCompleted += service_PowerpointAsByteCompleted;
                    service.PowerpointAsByteAsync(slides);
                    tbStatus.Text = "Saving file...";
                }
 
            }
            catch (Exception ex)
            {
                ShowErrorAlert(string.Format("Error while getting powerpoint file:\n{0}", ex.Message));
            }

On completion of the WCF service call:

   void service_PowerpointAsByteCompleted(object sender, PowerpointAsByteCompletedEventArgs e)
         {
            if (e.Error != null)
            {
                ShowErrorAlert(string.Format("Error while getting powerpoint file:\n{0}", e.Error.Message));
                return;
            }
            if (!string.IsNullOrWhiteSpace(e.Result.ErrorMessage))
            {
                ShowErrorAlert(e.Result.ErrorMessage);
                return;
            }

            byte[] pptByte = e.Result.PptByte;

            if (pptByte == null)
            {
                ShowErrorAlert("Empty powerpoint file is returned from server.");
                return;
            }

            using (System.IO.Stream stream = SaveFileDialog.OpenFile())
            {
                stream.Write(pptByte, 0, pptByte.Length);
                tbStatus.Text = "File saved: " + SaveFileDialog.SafeFileName;
            }

        }


WCF Project

This project offers only one method PowerPointAsByte. And the service interface IPowerpoint:

[ServiceContract]
    public interface IPowerpoint
    {
 
        ///
<summary />
        /// Converts the images from images collection to powerpoint slides and return the powerpoint file as byte array
        /// </summary />
        /// <param name="slides" /></param />
        /// <returns /></returns />
        [OperationContract]
        PowerpointAsByteResult PowerpointAsByte(IList<powerpointimage /> images);
       
   
}


Service class Powerpoint :

  public class Powerpoint : IPowerpoint
    {
 
        ///
<summary />
        /// Method accepts collection of images and convert them as powerpoint slides.
        /// </summary />
        /// <param name="slides" /></param />
        /// <returns />byte array of powerpoint file</returns />
        public PowerpointAsByteResult PowerpointAsByte(IList<powerpointimage /> slides)
        {

            PowerpointAsByteResult result = new PowerpointAsByteResult();
            //Don't throw any exception across wire.
            try
            {
                result.PptByte = (new PowerpointHelper()).GetPowerpointAsByte(slides);
            }
            catch (Exception ex)
            {
                result.ErrorMessage = string.Format("Error while converting images to powerpoint:\n{0}",ex.Message);
            }
            return result;
           
       
}
              
   
}


Class Diagram of WCF Service

PwrSilver8.png

Something to remember before work with code:
  • You need Visual Studio 2010 to open this code
  • When you run the project you may receive the error message when making a WCF call. This is because the address of the WCF Service may change in your development server. So first run the WCF Service and take the address and update it in the ServiceReferences.ClientConfig file of Silver light project
  • If you deal with images of huge size, then you need to increase the upload size limit in the web.config file. Have a look at this link: http://forums.silverlight.net/forums/p/65327/433543.aspx

Conclusion

I believe this article will help at least a few people who need this functionality. Please test this code and post your comments for any issues/suggestions.


Similar Articles