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.
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:
IPowerPointServiceWrapper interface:
The following code block shows the IPowerPointServiceWrapper
interface. The functionality of each method is described in their code comment.
using System;
using System.Collections.Generic;
namespace PowerPointService
{
public interface IPowerPointServiceWrapper
{
void InitializeProgressReporter(Action<int, object> progressReporter);
void InitializeCancelationEventHandler(out EventHandler cancelationEventHandler);
void CancelationAsync(object sender, EventArgs e);
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.
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.
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.
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.
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 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.
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.
private bool IsCancelationPending = false;
public void InitializeCancelationEventHandler(out EventHandler cancelationEventHandler)
{
cancelationEventHandler = new EventHandler(CancelationAsync);
}
Implementation of the EventHandler
method inside of the PowerPointServiceWrapper
.
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.
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.
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.
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.
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.
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
).
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).
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.
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.
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.
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.
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.
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)
{
}
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.
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.
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