Click here to Skip to main content
15,867,568 members
Articles / Web Development / ASP.NET
Article

Converting WinForms => Web Forms using CodeDom

,
Rate me:
Please Sign up or sign in to vote.
4.82/5 (40 votes)
10 Feb 2005CPOL8 min read 272.3K   14.2K   139   23
Introducing a workable approach to converting Windows Forms to ASP.NET Web Forms.

Sample Image - WinForms2WebForms.gif

Contents

Introduction

Every so often, client projects have a requirement to provide both a Windows Forms and a browser-based front end. Usually, the Windows Forms application provides all the features whereas the HTML version is a somewhat "lightweight" variant of it. Since both types of user interfaces access the same business logic, it would be convenient to be able to convert Windows Forms UIs to Web Forms, thus saving development time which is otherwise spent on tedious duplication of existing Windows Forms in the Web Form Designer. This article demonstrates a possible approach to achieving a transformation of Windows Forms UIs to ASP.NET Web Forms.

Disclaimer

Please note that it is not the intention of this article or the accompanying code sample to achieve a complete conversion between Windows Forms and Web Forms including all events and business logic. Due to the fundamentally different nature of the two programming models, this would be a fruitless attempt. Rather, we are targeting the user interface components themselves, mapping Windows Forms controls to appropriate Web Forms counterparts. If you are binding your forms to business classes — and we are sure you do — you are still responsible for retrieving instances of them from the data store and writing back changes entered by the user.

Background

The main problem in attempting a direct conversion between Windows Forms and Web Forms is mapping controls and their properties to each other.

Let's take a System.Windows.Forms.TextBox control, for example. The obvious choice for its match in the System.Web.UI namespace would be a System.Web.UI.WebControls.TextBox control. That's easy. A closer look at the properties of the two types, however, reveals quite a number of mismatches: mapping a Windows Forms control's Name property to the ID property of its Web Forms counterpart isn't too difficult. But what about properties like Anchor, BindingContext and SelectedText? No matter how hard you try, you're not going to find corresponding Web Control properties. After all, Web Controls are ultimately rendered as HTML which only provides a small subset of the features available to Windows Forms applications. Therefore, you'll have to do without a significant number of Windows Forms properties, and limit yourself to the most basic ones, specifically those determining position and size.

How it works

The central class of the sample, Convert2Aspx, contains a single public method, Convert, and a number of properties to customize the Web Form conversion process. You can set the desired output language (SourceLanguage) and whether you want to create a page or a user control (AspxType).

The Convert method takes two arguments: the form or control to be converted and the path the output files are to be written to. It creates both an aspx/ascx file and a corresponding code-behind file, in the provided location. Control declarations are inserted in the aspx/ascx file, and appropriate methods and variable declarations get added to the code-behind, just the way Visual Studio .NET does it. Correct positioning is achieved using Cascading Style Sheets (CSS) instructions, which are correctly interpreted by all modern browsers.

Using the code

The Convert2Aspx class allows you to create web form pages (*.aspx) as well as user controls (*.ascx). You determine the output format by setting the AspxType enum property to either AspxTypes.Page or — you guessed it — AspxTypes.UserControl. Pick your target language of choice, C# or VB.NET, by setting the SourceLanguage property to the desired value, and call the Convert method. Two snippets illustrating the use of the class follow below.

Converting a System.Windows.Forms.Form instance to a Web Forms page:

C#
convert2Aspx = new suite4.net.WinForms2WebForms.Convert2Aspx();
convert2Aspx.AspxType = suite4.net.WinForms2WebForms.AspxTypes.Page;
convert2Aspx.SourceLanguage = suite4.net.WinForms2WebForms.SourceLanguages.C_Sharp;
convert2Aspx.Convert(this, @"C:\WinForms2WebForms");

Converting a System.Windows.Forms.GroupBox instance to a Web Forms user control:

C#
convert2Aspx = new suite4.net.WinForms2WebForms.Convert2Aspx();
convert2Aspx.Namespace = "suite4.net.WinForms2WebForms";
convert2Aspx.RootName = "grpAdress";
convert2Aspx.FullName = convert2Aspx.Namespace + "." + convert2Aspx.RootName;
convert2Aspx.AspxType = suite4.net.WinForms2WebForms.AspxTypes.UserControl;
convert2Aspx.SourceLanguage = suite4.net.WinForms2WebForms.SourceLanguages.C_Sharp;
convert2Aspx.Convert(this.grpAdress, @"C:\WinForms2WebForms");

Notice how in the second example the Namespace, RootName and FullName properties are explicitly set. Doing so allows you to manipulate various naming-related properties in the conversion output. If you simply call Convert without bothering about them, the property values are read from the form or control passed to the method as the first argument.

Using the sample

The demo project accompanying this article takes a simple Windows Form and converts it to both a Web Form (as shown at the top of this page) and a Web Forms user control. To see the conversion in action, do the following:

  1. Create a directory named "WinForms2WebForms" on your C: drive.
  2. Using Internet Information Services (IIS) Manager, create a virtual directory pointing to the folder created in step 1. Name it "Win2Web" or anything else that suits your fancy.
  3. Open Visual Studio .NET and create a new ASP.NET web project in C#. Enter http://localhost/<Name-from-Step2>/ as the storage destination. Visual Studio .NET will create a new web project and store the files in C:\WinForms2WebForms.
  4. Delete WebForm1.aspx from the project.
  5. Open this article's demo project and run it. Click "Write to aspx" when the sample form appears. The Web Form pages and code-behind files are written to the directory designated in step 1.
  6. Go back to the web project and click View > Refresh in Visual Studio's main menu. The newly created files will now be visible in the Project Explorer. If they don't appear, click Project > Show all files. You should now be able to see them in the Project Explorer.
  7. Right-click on each of the new files and include them in the project.
  8. Build and run the project after you have set Form1.aspx as the start page. Your browser will display the converted form.

Code generation

The conversion process entails creating two different file types for each Web Forms page or user control:

  1. the aspx/ascx file, and
  2. its corresponding code-behind file as a C# or Visual Basic class file.

Let's have a quick look under the hood of generating each of them.

Creating the aspx/ascx file

For the aspx/ascx file, we opted to generate the code using a System.Text.StringBuilder class. The generation is pretty straight-forward and consists of inserting the common page elements like the <html>, <head> and others, and then looping through each control placed on the Windows Form to generate its ASP.NET declaration, such as <asp:TextBox id="myTextbox" runat="server" style="[...]" />.

The reason we chose to go with a simple StringBuilder for this task is that the aspx/ascx files are pretty much language-independent and C#/VB.NET syntax is not relevant for them. All that needs to be adjusted is the <%@ Page [...] %> declaration at the top of the file, the remaining content is dominated by HTML and ASP.NET control declarations.

The snippet below is taken from the ConvertControls method of the Convert2Aspx class, to illustrate the basic conversion algorithm:

C#
foreach(System.Windows.Forms.Control control in rootControl.Controls)
{
    if(control is System.Windows.Forms.Label)
    {
        webLabel = new System.Web.UI.WebControls.Label();
        webLabel.ID = control.Name;
        this._WebControls.Add(webLabel);
        stringBuilder.Append(" <asp:Label");
        this.AddProperties(control, stringBuilder);
        stringBuilder.AppendFormat(">{0}</asp:Label>{1}", control.Text, 
            System.Environment.NewLine);
    } 
    else if(control is System.Windows.Forms.TextBox)
    {
        webTextBox = new System.Web.UI.WebControls.TextBox();
        webTextBox.ID = control.Name;
        this._WebControls.Add(webTextBox);
        stringBuilder.Append(" <asp:TextBox");
        this.AddProperties(control, stringBuilder);
        stringBuilder.AppendFormat(">{0}</asp:TextBox>{1}", control.Text, 
            System.Environment.NewLine);
    } 
    (...)
}

The excerpt converts System.Windows.Forms.Label and System.Windows.Forms.TextBox controls to their ASP.NET counterparts. The AddProperties method used above is responsible for assigning the required CSS style instructions to the ASP.NET control declaration. It looks like this:

C#
private void AddProperties(System.Windows.Forms.Control control, 
                System.Text.StringBuilder stringBuilder)
{
    stringBuilder.AppendFormat(" id=\"{0}\"", control.Name);
    stringBuilder.AppendFormat(" style=\"z-index:{0}; " + 
        "left:{1}px; top:{2}px; font-family:'{3}'; " + 
        "font-size:{4}pt; position:absolute;\"", 
        this._ZIndex++, control.Left, control.Top, 
        control.Font.FontFamily.Name, (int)control.Font.Size);
    stringBuilder.Append(" runat=\"server\"");
    stringBuilder.AppendFormat(" Width=\"{0}\"", control.Width);
    stringBuilder.AppendFormat(" Height=\"{0}\"", control.Height);

    if(control is System.Windows.Forms.TextBox)
    {
        stringBuilder.AppendFormat(" TabIndex=\"{0}\"", control.TabIndex);
    }
}

So far, so good. We now know how to properly deal with Label and TextBox controls. But what if they are not simply child controls of the main form but are themselves part of, say, a System.Windows.Forms.Panel or System.Windows.Forms.GroupBox? The answer is — quite obviously — recursion. The following snippet is again taken from the ConvertControls method and illustrates how to go about converting a System.Windows.Forms.GroupBox:

C#
else if(control is System.Windows.Forms.GroupBox)
{
    stringBuilder.Append("<fieldset");
    stringBuilder.AppendFormat(" ID=\"{0}\" runat=\"server\"", control.Name);
    stringBuilder.AppendFormat(" style=\"POSITION:" + 
        " absolute; left: {0}px; top: {1}px; width:{2}px; 
        height: {3}\"", control.Left, control.Top, 
        control.Width, control.Height);
    stringBuilder.AppendFormat(">{0}", System.Environment.NewLine);
    stringBuilder.Append("<legend");
    stringBuilder.AppendFormat(" style=\"Z-INDEX: {0}; " + 
        "color:black; font-family:'{1}'; font-size:{2}pt; width=\"", 
        this._ZIndex++, control.Font.FontFamily.Name, 
        (int)control.Font.Size);
    stringBuilder.AppendFormat(">{0}</legend>{1}", 
                  control.Text, System.Environment.NewLine);
    this.ConvertControls(control, stringBuilder);
    stringBuilder.AppendFormat("</fieldset>{0}", 
                          System.Environment.NewLine);
    webGroupBox = new 
      System.Web.UI.HtmlControls.HtmlGenericControl("fieldset");
    webGroupBox.ID = control.Name;
    this._WebControls.Add(webGroupBox);
}

Here, we're taking advantage of HTML's relatively little-known <fieldset> tag to emulate the visual appearance of the System.Windows.Forms.GroupBox control. You can view the result in the screenshot at the top of this page.

The sample class Convert2Aspx currently only converts System.Windows.Forms.Label, System.Windows.Forms.TextBox and System.Windows.Forms.GroupBox controls to their ASP.NET counterparts. However, using the principles outlined in this article, support for further control types can easily be added.

Creating the code-behind file

Code-behind files are written entirely in C# or VB.NET and, therefore, require thorough consideration of syntax and language features. Generally, there are two common options for approaching C#/VB.NET code generation:

  1. Create a template with placeholders for the controls and other parameters, fill the placeholders dynamically with the Windows Form control properties. Disadvantage: you have to create and maintain one template for each required output language, resulting in potential synchronization faults if you make a change to one template and forget to amend the other or others, too.
  2. Use the classes from the System.CodeDom namespace to dynamically create the code-behind files in the correct syntax for either language. Maintenance is no problem with this approach since there is only one source file to be maintained — the one containing your source code.

Since templates are limited in use and can become quite unwieldy as their complexity increases, we opted for using CodeDOM for generating the code-behind files. This is no article about the use of CodeDOM, so we're not going to go into too much detail here, but as you'll see, the code is quite self-explanatory. Here's an excerpt from the Convert2Aspx class which creates a Page_Load method in the code-behind file:

C#
private void BuildPageLoadMethod(System.CodeDom.CodeTypeDeclaration 
                                                            typeDeclaration)
{
  System.CodeDom.CodeMemberMethod codeMethodPageLoad;
  System.CodeDom.CodeParameterDeclarationExpression 
                             codeParameterExpression;

  // Add Page_Load method
  codeMethodPageLoad = new System.CodeDom.CodeMemberMethod();
  codeMethodPageLoad.Name = "Page_Load";

  // Add sender parameter
  codeParameterExpression = new 
      System.CodeDom.CodeParameterDeclarationExpression(typeof(object), 
      "sender");
  codeMethodPageLoad.Parameters.Add(codeParameterExpression);

  // Add eventargs parameter
  codeParameterExpression = new 
      System.CodeDom.CodeParameterDeclarationExpression(
                         typeof(System.EventArgs), "e");
  codeMethodPageLoad.Parameters.Add(codeParameterExpression);
  typeDeclaration.Members.Add(codeMethodPageLoad);
}

The above statements create an empty method that accepts two parameters and produces the following output in C#:

C#
private void Page_Load(object sender, System.EventArgs e)
{
}

You can find a number of similar methods in the Convert2Aspx class that all work essentially the same way. Studying them will soon give you a feeling for how to "think" with the CodeDOM. And the big advantage of using it is that changing or expanding functionality in the generated code requires modifications at a single location only.

Revision History

  • 14 Jan 2005: Original article published.
  • 09 Feb 2005: Update submitted which includes the following changes:
    • Added support for nested controls.
    • Added a clarification of the procedure for using the sample code.
    • Added table of contents for the article.
    • Various minor edits and corrections.

License

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


Written By
Software Developer (Senior) M-net Telekommunikations GmbH
Germany Germany
.NET developer based in Munich, Germany. Specialising in development automation and code generation for software developers using .NET.

Owner and Developer of form.suite4.net

Written By
Web Developer
Germany Germany
Hardy is an independent consultant and developer located in Munich, Germany. He spezializes in ASP.NET development and runs the .NET Developers Group Munich since April 2003.

Company website: www.netspectrum.de

Comments and Discussions

 
GeneralRe: similar tool Pin
ASommer11-Feb-05 8:52
ASommer11-Feb-05 8:52 
GeneralRe: similar tool Pin
Pedro M. C. Cardoso11-Feb-05 13:40
Pedro M. C. Cardoso11-Feb-05 13:40 
GeneralRe: similar tool Pin
ASommer11-Feb-05 13:49
ASommer11-Feb-05 13:49 
GeneralMS LongHorn model ......... Pin
sandurea8-Feb-05 2:00
sandurea8-Feb-05 2:00 
GeneralRe: MS LongHorn model ......... Pin
ASommer9-Feb-05 4:00
ASommer9-Feb-05 4:00 
GeneralProblems Using the sample Pin
vbman2004x23-Jan-05 8:31
vbman2004x23-Jan-05 8:31 
GeneralRe: Problems Using the sample Pin
Hardy Erlinger23-Jan-05 11:59
Hardy Erlinger23-Jan-05 11:59 
GeneralRe: Problems Using the sample Pin
dblanchard23-Jun-06 12:22
dblanchard23-Jun-06 12:22 
GeneralRe: Problems Using the sample Pin
dblanchard23-Jun-06 13:20
dblanchard23-Jun-06 13:20 
GeneralDeclarative Programming Pin
Marc Clifton15-Jan-05 7:42
mvaMarc Clifton15-Jan-05 7:42 
GeneralRe: Declarative Programming Pin
ASommer16-Jan-05 3:54
ASommer16-Jan-05 3:54 
GeneralRe: Declarative Programming Pin
D A R Kemp18-Jan-05 23:56
D A R Kemp18-Jan-05 23:56 
GeneralRe: Declarative Programming Pin
Marc Clifton19-Jan-05 3:50
mvaMarc Clifton19-Jan-05 3:50 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.