Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / desktop / WinForms

PowerPoint template generator

5.00/5 (1 vote)
7 Aug 2014CPOL17 min read 21.2K   341  
This article describes a windows forms application which automates MS Office PowerPoint 2007 in order to replace predefined text of a template with your input

Introduction

Do you have to prepare several PowerPoint presentations? Do you be tired to change all the time all the repeating content of all the slides and master pages? Maybe the described application in this article will help you a little bit.

With help of this application you can personalize every PowerPoint 2007 template file.

Image 1

Techniques

Additionally this small and simple application includes several techniques that may be useful for you. This article should show you how to use that techniques all together packed in one single article.

The following list shows the implemented techniques with a short description:

  • Automate Microsoft PowerPoint
    With the help of the assemblies Microsoft.Office.Interop.PowerPoint and Office the PowerPoint will be automated in order to load a PowerPoint template and replace some text in nearly all parts of the whole PowerPoint presentation. The automation of PowerPoint is separated of the application within an own project. The project has a clear documented interface so it will be easy to reuse that project.
  • Cancelation of long lasting method calls with EventHandler
    To be able to cancel the replace workflow relatively fine-grained an EventHandler will be used. With an interface method the EventHandler can be fetched from inside the PowerPointServiceWrapper object and if the EventHandler will be called from outside an internal flag can be set so that the PowerPointServiceWrapper object knows that a cancel request was sent. After the actual fine-grained task was finished the replace workflow will be interrupted and the method call will return immediately.
  • Report progress to the caller
    The issue to report progress relatively fine-grained is very similar to the cancelation topic. But here we have to think the other way around. Now we have to put the information from inside the PowerPointServiceWrapper object out to the caller. In the actual situation we have to put the progress information to the ReportProgress method of the BackgroundWorker. The solution here is to put the ReportProgress method inside of the PowerPointServiceWrapper object. This can be done by putting the ReportProgress method to an internal member of type System.Action<int, object>.
  • BackgroundWorker
    The replace workflow is running within a BackgroundWorker. This will enhance the user experience in order to have a working progress bar, the possibility to cancel the workflow and the window will not be freeze during the replace workflow is running.
  • Drag and Drop Files from windows explorer to the user interface
    The PowerPoint template file can be chosen by drag and drop from the windows explorer to the window. This article shows you the usage of the drag and drop function.
  • StatusStrip with status information and progress bar
    The StatusStrip control is typical used for status information. In this application it is used to show the progress bar during the replace workflow and also the status information (which step of the workflow is actually processed). The usage of the StatusStrip control is very easy and there is nothing special. So it is just listed here and will not be deeper explained.
  • Use of UserSettings to restore data on application start
    That the user mustn’t always type in all the replacement information the text boxes are filled during application start from the restored user settings which are stored during the last replace workflow.
  • Fetch information from current user
    If you start the application the first time the replacement information are filled with default values. The user name, its phone number and its email address will be fetched from the system. Therefore System.DirectoryServices.AccountManagement.UserPrincipal.Current will be used.
  • Creation and use of UserControls
    As you can see from the attached screen shot the user interface has a lot of rows with the following controls. A checkbox if that replacement information should be used or not, a label which helps the user to refer each replacement information to the right place in the PowerPoint template and two text boxes with the replacement information itself. Each of those rows is encapsulated in one UserControl which can be easily put to the form.

The techniques of the above list will later be explained in more detail.

Operating principle

If you download the attached zip file, you will find a TestTemplate.potx file. In the TestTemplate.potx you can see the words like SET_YOUR_NAME_HERE, SET_TITLE_HERE, ... which will be replaced by your input in all slides, master pages, layouts of the finally shown PowerPoint presentation. In this case for example the word SET_YOUR_NAME_HERE will be replaced with roli.hof. These placeholders can be changed and adapted to your needs.

The program works as simple text replacement. This will make you totally flexible for your need. The labels are just for structural help. With the help of the check box in front of each line you can enable or disable each replacement configuration separate.

The inputs that you will make in the UI will be saved to the user settings when you press the Create Presentation button. So you have to make the most inputs only once a time. If you start the application next time the inputs from your last run will be restored from the user settings.

The application has a Close button. During the replacement workflow the label of the button changes to Cancel. With that button you can either close the window or if the replacement workflow is actually running, you can cancel the replacement workflow.

The progress is shown in the StatusStrip on the bottom of the window.

The PowerPoint template file can be chosen either with an OpenFileDialog or with Drag and Drop from the file explorer.

Test environment

The program is tested on WIN7 x64 with installed MS Office 2007. As develop environment Visual Studio 2010 is used.

Background

As already mentioned the interaction with PowerPoint is located in a separate project. So it will be easy to make this application also running for other MS office versions. Or even with completely different document types. Therefore you have to implement just one class that is similar to the PowerPointServiceWrapper class which implements the IPowerPointServiceWrapper interface. The PowerPointServiceWrapper is equipped with a possibility to report progress to the caller and the caller is also able to fire a cancelation event to cancel the replacement job.

Using the code

If you download the zip file you will find beside the TestTemplate.potx file two Visual Studio 2010 projects. One is the PowerPointServiceWrapper which encapsulates the handling with the PowerPoint application. The other one is the PowerpointTemplateGenerator where the user interface is implemented. The following part will show you the PowerPointServiceWrapper project in detail. Later we will have a look on the application itself.

Automate Microsoft PowerPoint

Class diagram of PowerPointServiceWrapper project:
Image 2

IPowerPointServiceWrapper interface:
The following code block shows the IPowerPointServiceWrapper interface. The functionality of each method is described in their code comment.

C#
using System;
using System.Collections.Generic;

namespace PowerPointService
{
  public interface IPowerPointServiceWrapper
  {
    /// <summary>
    /// Initializes the progress reporter.
    /// Call this method with the progress reporter method as parameter if you need to handle
    /// progress information.
    /// </summary>
    /// <param name="progressReporter">The progress reporter.</param>
    void InitializeProgressReporter(Action<int, object> progressReporter);
 
    /// <summary>
    /// Initializes the cancelation event handler.
    /// Call this method with an EventHandler as parameter if you need to react on cancelation events.
    /// </summary>
    /// <param name="cancelationEventHandler">The cancelation event handler.</param>
    void InitializeCancelationEventHandler(out EventHandler cancelationEventHandler);
 
    /// <summary>
    /// Cancelations the operation asynchronous.
    /// This method will be called internally if the initialized cancelationEventHandler will be called.
    /// This method is listed in the interface just that it will be not forgotten in case that someone
    /// will implement that interface for another usage than in this example.
    /// Normally there is no need to call This method manually.
    /// </summary>
    /// <param name="sender">The sender.
    /// Not needed.</param>
    /// <param name="e">The <see cref="EventArgs"/> instance containing the event data.
    /// Not needed</param>
    void CancelationAsync(object sender, EventArgs e);
 
    /// <summary>
    /// Runs the replacements.
    /// </summary>
    /// <param name="templateFileLocation">The template file location.</param>
    /// <param name="replaceInfoList">The replace information list.</param>
    /// <returns>true if everything was ok.</returns>
    bool RunReplacements(string templateFileLocation, List<ReplaceInfo> replaceInfoList);
  }
}

Initialization of cancelation handling and progress reporting:
The methods InitializeProgressReporter and InitializeCancelationEventHandler have to be called before the method RunReplacements. If you don't need the cancelation functionality or the progress reporter, then the RunReplacements method works also without these functionalities. Therefore just don't call the two initialization methods.

RunReplacementsmethod:
The main replacement workflow is implemented in the RunReplacements method. So let’s have a look on it.

C#
public bool RunReplacements(string templateFileLocation, List<ReplaceInfo> replaceInfoList)
{
  try
  {
    ProgressChanged(0, "Loading template ...");
    LoadPPTemplate(templateFileLocation);
    if (IsCancelationCalled(10, "Loading template finnished ..."))
    {
      return false;
    }

    ProgressChanged(11, "Processing Slides - Master pages ...");
    ReplaceInMasterPages(replaceInfoList);
    if (IsCancelationCalled(20, "Processing Slides - Master pages finnished ..."))
    {
      return false;
    }

    ProgressChanged(21, "Processing SlideMaster - Custom Layout ...");
    ReplaceInCustomLayout(replaceInfoList);
    if (IsCancelationCalled(40, "Processing SlideMaster - Custom Layout finnished ..."))
    {
      return false;
    }

    ProgressChanged(41, "Processing Slides ...");
    ReplaceInSlides(replaceInfoList);
    if (IsCancelationCalled(80, "Processing Slides finnished ..."))
    {
      return false;
    }

    ProgressChanged(81, "Processing TitleMaster ...");
    ReplaceInTitleMaster(replaceInfoList);
    if (IsCancelationCalled(90, "Processing TitleMaster finnished ..."))
    {
      return false;
    }

    ProgressChanged(91, "Processing SlideMaster ...");
    ReplaceInSlideMaster(replaceInfoList);
    if (IsCancelationCalled(99, "Processing SlideMaster finnished ..."))
    {
      return false;
    }

    MakePowerPointVisible(true);
    if (IsCancelationCalled(100, "Generation Finished!"))
    {
      return false;
    }

    return true;
  }

  catch (Exception ex)
  {
    ProgressChanged(100, "Generation with error: " + ex.Message);
    return false;
  }
}

Cancelation handling and progress reporting:
In the RunReplacements method there are several internal method calls that do the replacement work. Before each call we see the code for handling the progress information. Therefore the ProgressChanged method is called where it will be checked if the progress reporter was initialized and if it has been initialized the progress information will be put through to the caller. After each call we have the check if the replacement workflow should be canceled and again the progress information handling. Both actions are handled in the IsCancelationCalled method.

Loading the PowerPoint template:
The first thing in the RunReplacements method for doing the replacement work is to load the PowerPoint template file from the location that is specified in the input parameter templateFileLocation. Before loading the PowerPoint template file the PowerPoint application has to be initialized and PowerPoint has to be started. All these activities will be done in the LoadPPTemplate method. Following code block shows you the LoadPPTemplate method.

C#
private void LoadPPTemplate(string templateFilePath)
{
  app = new PP.Application();
  app.Visible = OFFICE.MsoTriState.msoTrue;
  presentaions = app.Presentations;
  presentation = presentaions.Open(templateFilePath, OFFICE.MsoTriState.msoTrue, OFFICE.MsoTriState.msoTrue, app.Visible);
}

Doing the replacements:
The PowerPoint object model (see PowerPoint object model reference at http://msdn.microsoft.com/en-us/library/bb251394(v=office.12).aspx) uses so called Shapes to manage text and other objects. In the MSDN reference you can use the link http://msdn.microsoft.com/en-us/library/bb265915(v=office.12).aspx that will show you how to work with shapes. If we want to change text in different slides, layouts, master pages and so on, we first have to fetch the shapes from all the different slides, layouts, master pages and so on and looks for a text inside of each shape that contains a text that we want to replace. The replaceInfoList contains the text that should be replaced and also the text that should be written instead. In a loop all replacement information will be searched and possibly replaced. This is of course not the fastest solution. But I decided to go this way because it will be very easy to make adaptations and it is also easy to write a reusable code. The following code block shows the ReplaceInTitleMaster method and the ReplaceInShapes method. The other method calls of the RunReplacements method will be very similar to the ReplaceInTitleMaster method. So it will not be listed here.

C#
private void ReplaceInTitleMaster(List<ReplaceInfo> replaceInfoList)
{
  if (presentation.HasTitleMaster == OFFICE.MsoTriState.msoTrue)
  {
    foreach (PP.Shape shape in presentation.TitleMaster.Shapes)
    {
      ReplaceInShapes(shape, replaceInfoList);
    }
  }
}

private void ReplaceInShapes(PP.Shape shape, List<ReplaceInfo> replaceInfoList)
{
  if (replaceInfoList != null)
  {
    foreach (ReplaceInfo replaceInfo in replaceInfoList)
    {
      if (IsCancelationCalled())
      {
        return;
      }
      if (shape.TextFrame.HasText == OFFICE.MsoTriState.msoTrue)
      {
        if (shape.TextFrame.TextRange.Text.Contains(replaceInfo.TextToReplace))
        {
          string text = shape.TextFrame.TextRange.Text;
          text = text.Replace(replaceInfo.TextToReplace, replaceInfo.TextReplaceWith);
          shape.TextFrame.TextRange.Text = text;
        }
      }
    }
  }
}

Usage of IPowerPointServiceWrapper interface:
The next code block shows the DoWork method of the background worker. Here you can see the usage of the IPowerPointServiceWrapper. First build the replaceInfoList, then allocate a new PowerpointServiceWrapper, then initialize the progress reporter and the handling for cancelation and then do the replacement workflow by calling the RunReplacement method.

C#
private void BackgroundWorker_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
{
  System.ComponentModel.BackgroundWorker worker = sender as System.ComponentModel.BackgroundWorker;
  List<ReplaceInfo> replaceInfoList = FillReplaceInfoList();
  IPowerPointServiceWrapper pp = new PowerPointServiceWrapper();
  pp.InitializeProgressReporter(worker.ReportProgress);
  pp.InitializeCancelationEventHandler(out CancelationEvent);

  // bool return value not used in this case!
  bool isOk = pp.RunReplacements(txtTemplateFile.Text, replaceInfoList);
 
  if (worker.CancellationPending == true)
  {
    e.Cancel = true;
    return;
  }
}

The replaceInfoList is a List of type ReplaceInfo. This simple class you can see in the next code block.

C#
namespace PowerPointService
{
  public class ReplaceInfo
  {
    public string TextToReplace { get; set; }
    public string TextReplaceWith { get; set; }
  }
}

Cancelation of long lasting method calls with EventHandler

The BackgroundWorker has built-in support for cancelation. In the DoWork method you can ask if cancelation of the worker was called. But in our case we have the long lasting workflow packed in a separate project and the workflow where it makes sense to support cancelation is within one single method (the RunReplacements method). So we cannot use the built in mechanism of the BackgroundWorker in our situation. It is necessary to bring the cancelation support inside the PowerPointServiceWrapper. In this application it is solved with the help of an EventHandler. A general information about the EventHandler can be found in the MSDN library (see http://msdn.microsoft.com/en-us/library/system.eventhandler(v=vs.100).aspx). In our case the IPowerPointServiceWrapper interface has a method named InitializeCancelationEventHandler which has an out parameter of type EventHandler. By calling the InitializeCancelationEventHandler method a new EventHandler will be allocated and assigned/attached to the out parameter cancelationEventHandler. If something calls then the EventHandler from outside, inside the PowerPointServiceWrapper the attached method will be called. In this method a flag will be set so that we can ask during the replacement workflow if the cancelation request was already triggered. The following code blocks will show the implementation.

Inside of PowerPointServiceWrapper

Boolean member is initial set to false.

C#
private bool IsCancelationPending = false;

public void InitializeCancelationEventHandler(out EventHandler cancelationEventHandler)
{
  cancelationEventHandler = new EventHandler(CancelationAsync);
}

Implementation of the EventHandler method inside of the PowerPointServiceWrapper.

C#
public void CancelationAsync(object sender, EventArgs e)
{
  IsCancelationPending = true;
}

The following code block shows the method which will check if the cancelation was already triggered or not. If it is triggered the progress will be set to 100 percent and the method will return true so that the calling method can cancel and return to the caller. The usage of this method can be seen in the ReplaceInShapes method for example.

C#
private bool IsCancelationCalled()
{
  if (IsCancelationPending == true)
  {
    ProgressChanged(100, "Generation aborted by user!");
  }
  return IsCancelationPending;
}

EventHandler related implementation of the caller.

In this case the synonym caller is the windows forms application where the IPowerpointServiceWrapper is used. First put a member of type EventHandler to your caller as you can see in the following code.

C#
private event EventHandler CancelationEvent;

Then call the InitializeCancelationEventHandler method and put the above CancelationEvent member as parameter. The only thing that is missing now is to call the CancelationEvent method in case the cancelation should be triggered. In this case we put this cancelation call into the click event of the cancel button as shown in the following code block.

C#
private void btnCancel_Click(object sender, EventArgs e)
{
  if (backgroundWorker.IsBusy)
  {
    if (backgroundWorker.WorkerSupportsCancellation == true)
    {
      toolStripStatusLabel1.Text = "Generation aborting ... please wait!";
      backgroundWorker.CancelAsync();
      if (CancelationEvent != null)
      {
        CancelationEvent(sender, e);
      }
      btnCancel.Enabled = false;
    }
  }
  else
  {
    this.Close();
  }
}

Report progress to the caller

To report progress to the user we have a similar situation as we already talked about the cancelation request. The BackgroundWorker already has the built-in functionality to report progress information but because we call only one long lasting method inside the DoWork method of the BackgroundWorker we cannot use this functionality straight forward. We have to do something to get the functionality inside the PowerPointServiceWrapper. In our case we will use the Action class which can be found in the System namespace (see http://msdn.microsoft.com/en-us/library/bb549311(v=vs.100).aspx). With that help, the ReportProgress method of the BackgroundWorker object can be used as parameter for the IPowerPointServiceWrapper interface method to put the ReportProgress method inside to the PowerPointServiceWrapper. So it will be possible to call that method in the internal implementation of the PowerPointServiceWrapper. Next we will have a look at the relevant implementation parts.

Inside of PowerPointServiceWrapper

First declare a member of type Action<int, object> (ReportProgressChangedDelegate). Then assign during the call of the interface method (InitializeProgressReporter) the ReportProgress method of the BackgroundWorker object to the new declared member. Then this member can be called. In this case it is encapsulated with a separate method (ProgressChanged). The check if the ReportProgressChangedDelegate is not null let us be able to makes the program running even no progress reporter will be initialized.

C#
private Action<int, object> ReportProgressChangedDelegate;

public void InitializeProgressReporter(Action<int, object> progressReporter)
{
  ReportProgressChangedDelegate = progressReporter;
}

private void ProgressChanged(int percent, string userState)
{
  if (ReportProgressChangedDelegate != null)
  {
    ReportProgressChangedDelegate(percent, userState);
  }
}

Progress related implementation of the caller.

In this case the synonym caller is the windows forms application where the IPowerpointServiceWrapper is used. First we have to attach the event handler to the ProgressChanged event of the BackgroundWorker object.

C#
private void InitializeBackgroundWorker()
{
  backgroundWorker = new System.ComponentModel.BackgroundWorker();
  backgroundWorker.WorkerReportsProgress = true;
  backgroundWorker.WorkerSupportsCancellation = true;
  backgroundWorker.DoWork += new System.ComponentModel.DoWorkEventHandler(BackgroundWorker_DoWork);
  backgroundWorker.ProgressChanged += new System.ComponentModel.ProgressChangedEventHandler(BackgroundWorker_ProgressChanged);
  backgroundWorker.RunWorkerCompleted += new System.ComponentModel.RunWorkerCompletedEventHandler(BackgroundWorker_RunWorkerCompleted);
}

Then we have to implement the newly attached method (BackgroundWorker_ProgressChanged).

C#
private void BackgroundWorker_ProgressChanged(object sender, System.ComponentModel.ProgressChangedEventArgs e)
{
  toolStripStatusLabel1.Text = e.UserState.ToString();
  if (e.ProgressPercentage == 0)
  {
    toolStripProgressBar1.Maximum = 100;
  }
  toolStripProgressBar1.Value = e.ProgressPercentage;
}

Then we have to call the InitializeProgressReporter method of the IPowerPointServiceWrapper to put the ReportProgress method of the BackgroundWorker object into the PowerPointServiceWrapper object. This is done in the BackgroundWorker_DoWork method (see code snippet some lines above).

C#
pp.InitializeProgressReporter(worker.ReportProgress);

BackgroundWorker

The BackgroundWorker is well documented in the MSDN library (see http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker(v=vs.100).aspx). This documentation contains also examples of how you can use the BackgroundWorker. So you will find here just a rough check list what you have to do.

  • First allocate a new Backgroundworker
  • Attach EventHandlers (DoWork, ProgressChanged, RunWorkerCompleted)
  • Set WorkerReportsProgress to true if you want use progress reporting
  • Set WorkerSupportsCancellation to true if you need cancelation possibility
  • Implement the methods defined in the EventHandlers
  • Call the RunWorkerAsync method of the BackgroundWorker object

Drag and Drop Files from windows explorer to the user interface

In this application it is possible to drag and drop a PowerPoint template file from the windows explorer to the textbox where the file location of the template file can be inserted. To use drag and drop we have to do several things. Additional information for drag and drop functionality can be found in following link http://support.microsoft.com/kb/307966/en. First we have to change the property AllowDrop of the textbox to true. Then we have to attach two events. One is the DragEnter and the other one is the DragDrop event. Then the methods for these two events have to be implemented. The DragEnter event occurs when the file will be dragged inside the bounds of the textbox. Here we tell the application that it is an copy process. This results in the change of the mouse cursor to show a plus sign for copying.

C#
private void txtTemplateFile_DragEnter(object sender, DragEventArgs e)
{
  e.Effect = DragDropEffects.Copy;
}

The DragEnter event occurs when we letting go of the mouse button. Here we inspect the Data member of the DragEventArgs. Then we look if in this data we will find a FileDropList and if yes then we take the first entry and assign that to the textbox. See the code below.

C#
private void txtTemplateFile_DragDrop(object sender, DragEventArgs e)
{
  DataObject data = (DataObject)e.Data;
  if (data.ContainsFileDropList())
  {
    string[] rawFiles = (string[])e.Data.GetData(DataFormats.FileDrop);
    if (rawFiles != null)
    {
      txtTemplateFile.Text = rawFiles[0];
      Properties.Settings.Default.TemplateFile = txtTemplateFile.Text;
      Properties.Settings.Default.Save();
      txtTemplateFile.Refresh();
    }
  }
}

Use of UserSettings to restore data on application start

In the MSDN library we can find information about application settings and user settings in the following link http://msdn.microsoft.com/en-us/library/0zszyc6e(v=vs.100).aspx. In this application the user settings are used to restore the user input of the last run during start of the application. So the user mustn’t always insert everything from scratch. One way to define UserSettings in Visual Studio 2010 would be to use the properties page of the project. Here you can find the tab Settings where you can insert new settings and define the data type and the scope. In our situation we use for all settings the data type string and User for the scope. In the constructor of the form we are restoring the settings of the last session or initialize them with default values (for the first start of the application on your system). The settings can be accessed with Properties.Settings.Default. See code below.

C#
public Form1()
{
  InitializeComponent();

  InitializeBackgroundWorker();

  Properties.Settings set = Properties.Settings.Default;

  InitializeSettingsIfNotAvailable(set);

  InitializeReplaceTextControls(set);
  txtTemplateFile.Text = set.TemplateFile;
}

private static void InitializeSettingsIfNotAvailable(Properties.Settings set)
{
  if (set.NameReplace == null || set.NameReplace.Length <= 0)
  {
    .
    .
    .
    set.TitleReplace = "SET_TITLE_HERE";
    set.Title = "Title";
    .
    .
    .
    set.Save();
  }
}

private void InitializeReplaceTextControls(Properties.Settings set)
{
  .
  .
  .
  SetControl(rtcTitle, "Title:", set.TitleReplace, set.Title, true);
  .
  .
  .
}

The settings will be mainly saved when the replacement workflow was started. To store the settings we have to call the Save method of Properties.Settings.Default. See code below.

C#
private void btnCreatePresentation_Click(object sender, EventArgs e)
{
  if (txtTemplateFile.Text != null && txtTemplateFile.Text.Length > 0)
  {
    Progress("Generation started!", 0);
    ButtonsEnabled(false);
    SaveProperties();
    .
    .
    .
}

private void SaveProperties()
{
  Properties.Settings set = Properties.Settings.Default;
  .
  .
  .
  set.Title = rtcTitle.TextReplaceWithThat;
  set.TitleReplace = rtcTitle.TextToReplace;
  .
  .
  .
  set.Save();
}

Fetch information from current user

In the method InitializeSettingsIfNotAvailable some information of the current logged on user will be fetched from the system and will be used as default values. This is only in case of the first start of the application on the system. Therefore the System.DirectoryServices.AccountManagement namespace will be used. To use that namespace you have to add the appropriate reference. In this namespace we can use UserPrincipal.Current to fetch the needed data from the actual user. Additional information can be found at http://msdn.microsoft.com/en-us/library/system.directoryservices.accountmanagement.userprincipal.current(v=vs.100).aspx. The following code shows you the usage of fetching user information.

C#
private static void InitializeSettingsIfNotAvailable(Properties.Settings set)
{
  if (set.NameReplace == null || set.NameReplace.Length <= 0)
  {
    AM    AM.UserPrincipal user = null;
    try
    {
      user = AM.UserPrincipal.Current;
    }
    catch (Exception)
    {
      // If the query will not work, then do nothing. The user has to insert the values manually.
    }

    set.NameReplace = "SET_YOUR_NAME_HERE";
    if (user != null && user.GivenName != null && user.Surname != null)
    {
      set.Name = user.GivenName + " " + user.Surname;
    }
    .
    .
    .
    set.PhoneReplace = "SET_YOUR_PHONENUMBER_HERE";
    if (user != null && user.VoiceTelephoneNumber != null)
    {
      set.Phone = user.VoiceTelephoneNumber;
    }

    set.EmailReplace = "SET_YOUR_EMAIL_HERE";
    if (user != null && user.EmailAddress != null)
    {
      set.Email = user.EmailAddress;
    }
    .
    .
    .
  }
}

Creation and use of UserControls

The UI for defining which text should be replaced with which other text is implemented by using a checkbox, a label and two textboxes. Of course there would be many other ways to solve that problem. But this application is grown up from a very simple one and I didn’t change the architecture of the UI in this topic. But it was very early clear that these four controls in the same structure will be needed not only one time and so I decided to encapsulate those four controls in one reusable UserControl. In the MSDN library the link http://msdn.microsoft.com/en-us/library/aa302342.aspx provides some information how to create an UserControl. But in this article the implementation language is Visual Basic. And so I’ll provide you with an additional link http://www.akadia.com/services/dotnet_user_controls.html. In my opinion this article is a good and understandable starting point.

To create a UserControl go in Visual Studio to the Solution Explorer and right click on your project where you want to put your new UserControl. Now search for Add and there choose User Control.... Visual Studio will then create all for you that you can start to add your controls to the panel. Now design your UserControl - add all needed controls and define the layout of your UserControl. Now view the code of your UserControl and add properties (with getter and setter) to the UserControl. With these properties you can now access the controls inside the UserControl from extern. See following code of the user control that is used in this application.

C#
using System.Windows.Forms;

namespace PowerpointTemplateGenerator
{
  public partial class ReplaceTextControl : UserControl
  {
    public bool IsActive
    {
      get { return this.chkActivated.Checked; }
      set { this.chkActivated.Checked = value; }
    }
    public string ControlName
    {
      get { return this.lblParameterName.Text; }
      set { this.lblParameterName.Text = value; }
    }
    public string TextToReplace
    {
      get { return this.txtReplaceThat.Text; }
      set { this.txtReplaceThat.Text = value; }
    }
    public string TextReplaceWithThat
    {
      get { return this.txtNew.Text; }
      set { this.txtNew.Text = value; }
    }
    public ReplaceTextControl()
    {
      InitializeComponent();
    }
  }
}

In the main form the access to the UserControl is encapsulated in two methods. See following code.

C#
private void SetControl(ReplaceTextControl rtc, string labelName, string textToReplace, string replaceWithThat, bool isActive)
{
  rtc.ControlName = labelName;
  rtc.TextToReplace = textToReplace;
  rtc.TextReplaceWithThat = replaceWithThat;
  rtc.IsActive = isActive;
}

private List<ReplaceInfo> FillReplaceInfoList()
{
  List<ReplaceInfo> replaceInfoList = new List<ReplaceInfo>();
  foreach (Control control in this.Controls)
  {
    if (control is ReplaceTextControl)
    {
      ReplaceTextControl rtc = control as ReplaceTextControl;

      if (rtc.IsActive)
      {
        ReplaceInfo ri = new ReplaceInfo();
        ri.TextToReplace = rtc.TextToReplace;
        ri.TextReplaceWith = rtc.TextReplaceWithThat;
        replaceInfoList.Add(ri);
      }
    }
  }
  return replaceInfoList;
}

Points of Interest

There are always several ways to solve problems. The attached solution was grown up from a very simple approach. For example on the very beginning the whole implementation was located in the Form1 class without having BackgroundWorker, no progress information any cancelation possibility and so on. But I found out that with such an ugly coding nothing will be reusable and so I spent some time on it to make it a little bit better. And now if I have the need to implement a simple tool I can use the attached application as a good starting point where I can copy the parts that I need from it. So the little bit more effort to make it more reusable will be paid back very soon.

History

05 August 2014 – Initial article
07 August 2014 – Update article to increase the depth of details

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)