Converting an Outlook Form Template (OFT) to a .NET Windows form in Visual Studio
In one of my work projects, I was assigned with the task of developing a standalone Windows form application to replace what was initially implemented as an Outlook Form Template (OFT) file using Visual Basic for Application (VBA) for data processing. The objective is to provide the user with an interface to fill in the required data and have them checked for validity before submitting.
Since the original form has over 500 fields, consisting of text boxes, dropdown lists, radio buttons and check boxes, it would be a nightmare to start designing the new form from scratch using Visual Studio form designer. My first attempt is to open the original form in Design mode in Outlook, copy the fields and paste to Visual Studio form designer. This did not work – probably because the clipboard format is different. I decided to find a method for me to copy the fields from the original Outlook form over to save time.
Importing the form in Visual Studio
I came across this MSDN article indicating the possibility of importing an Outlook form to be used in Visual Studio, and decided to attempt it.
First open the form in Design mode in Outlook:
Add a new Outlook form region and pasted all controls from the original form to the region:
After that, save the Outlook form region to an .OFS (Outlook Form Storage) file on the hard drive.
Finally, in a Visual Studio Outlook add-in project, add a new Outlook Form Region and choose to import from an existing Outlook Form Storage file:
Most of the wizard settings can be kept as default, except for the “Select the type of form region you want to create page” and the “Which custom message classes will display this form region” option, which should be Replace-all and IPM.Task.XXXX where XXXX is any valid name for your form region respectively.
After completing the wizard, a set of form region files, including the .designer.cs file, were added to the project.
Designer support for imported form regions
However, to my disappointment, despite the presence of the designer class, Visual Studio did not open this form region in the designer and simply opened the code editor for the designer class file. If however in the wizard I chose to design a new form region, Visual Studio would allow me to design the form region user interface. The difference is shown in the icons of the form region in the Solution Explorer:
Although this seems to be a common problem and can usually be fixed by reopening Visual Studio, cleaning and rebuilding the solution, in this case, I could not get FormRegion1 to open in the designer despite trying various workarounds.
The difference seems to be in the base class of the 2 forms. FormRegion2 inherits from Microsoft.Office.Tools.Outlook.FormRegionBase whereas FormRegion1 inherits from Microsoft.Office.Tools.Outlook.ImportedFormRegionBase and cannot be opened in the Designer. In fact while the generated for FormRegion2 contains all the necessary information to render the form fields at runtime, the code for FormRegion1 only contains minimal type declaration for the form controls, with most other information being retrieved from the .OFS file at runtime. This explains why the designer does not open FormRegion1 – it simply cannot be manipulated easily this way.
partial class FormRegion1 : Microsoft.Office.Tools.Outlook.ImportedFormRegionBase
{
private Microsoft.Office.Interop.Outlook._DRecipientControl to;
….
protected override void InitializeControls()
{
this.to = (Microsoft.Office.Interop.Outlook._DRecipientControl)GetFormRegionControl(“To”);
….
}
….
[System.Diagnostics.DebuggerNonUserCodeAttribute()]
byte[] Microsoft.Office.Tools.Outlook.IFormRegionFactory.GetFormRegionStorage(object outlookItem, Microsoft.Office.Interop.Outlook.OlFormRegionMode formRegionMode, Microsoft.Office.Interop.Outlook.OlFormRegionSize formRegionSize)
{
System.Resources.ResourceManager resources = new System.Resources.ResourceManager(typeof(FormRegion1));
return (byte[])resources.GetObject(“Survey”);
}
….
}
At this point it became clear to me that it would not be possible to open the imported FormRegion1 in the Designer and I had to come up with a different method to copy the form fields.
Exporting the form controls
I then had an idea of enumerating through the form controls at run-time and outputting them to a format readable by Visual Studio. The best candidate for the file format would be VB6 form (.frm) file. This format is human-readable, can be easily constructed from code and can be upgraded to a .NET Windows form using the VB6 upgrade wizard in Visual Studio 2008 and earlier.
The best place to do this would be in the FormRegionShowing event:
private void FormRegion1_FormRegionShowing(object sender, System.EventArgs e)
{
UserForm oForm = this.OutlookFormRegion.Form;
foreach (Control ctl in oForm.Controls)
{
if (ctl is Outlook.OlkCommandButton)
{
Outlook.OlkCommandButton cmdButton = (Outlook.OlkCommandButton)ctl;
string buttonTemplate = String.Format(@”
Begin VB.CommandButton {0}
Caption = “”{1}””
Height = {2}
Left = {3}
TabIndex = {4}
Top = {5}
Width = {6}
End”,
ctl.Name.Replace(” “, “”), escapeString(cmdButton.Caption), (int)(ctl.Height * 20), (int)(ctl.Left * 20), count, (int)(ctl.Top * 20),
(int)(ctl.Width * 20));
………….
}
}
}
The generated .frm code for the button would look like:
Begin VB.CommandButton Command1
Caption = “Command1″
Height = 855
Left = 480
TabIndex = 3
Top = 3480
Width = 2175
End
Using this method I was eventually able to generate the VB6 equivalent of all the forms in the original Outlook template and ugprade them to .NET forms. With some modifications, the upgrade forms are ready for further development work. The VBA code-behind of the forms can be upgraded using Telerik only code converter tool.
Potential issues with the conversion
Although it works well enough for my needs, this method is far from perfect. Differences in the possible measurement units (pixel, points, twips) used by Outlook and VB6 for the form fields cause the generated VB6 forms to look slightly different from the original form. Also, some imported controls belong to the namespace Microsoft.Vbe.Interop.Forms while others belong to the Microsoft.Office.Interop.Outlook namespace. This results in the need to use dynamic data type in my code to avoid unnecessary type casting. Finally, items in a combo box are not included in the .frm file, but stored in a separate .frx file:
Begin VB.ComboBox Combo1
Height = 315
ItemData = “Form1.frx”:0000
Left = 3960
List = “Form1.frx”:0010
TabIndex = 5
Text = “Combo1″
Top = 480
Width = 1215
End
Since the frx file is binary, I did not attempt to generate it and simply copy the combo box items manually. For simplicity, several other less common form control properties are also not migrated and are set manually after the conversion process.
The prototype code to generate the VB6 form can be downloaded here for those who are interested.