Building Windows Forms On-The-Fly With XML and C#
Building Windows Forms On-The-Fly With XML and C#
Programme
Search C#Today
Living Book
i Index
j
k
l
m
n j Full Text
k
l
m
n
Advanced
ABSTRACT Article
In this article, Ashiwn Kamanna takes the concept of an XML driven Form builder a step further than the Usefu
basics as he discusses how to eliminate the requirement for Form development, not only in an ASP
based web application, but also in any potential client of an application. He discusses how to build a Innov
Form dynamically in a C# based windows application, and also discusses some object oriented patterns
as he walks us through an example. Inform
14 res
ARTICLE
Editor's Note: This article's code has been updated to work with the final release of the .Net framework.
If we cannot completely stop the building of the entire Graphic User Interface, we can certainly reduce a lot of the
burden on the Form developers. In his ASPtoday article article, Owen Cutajar demonstrated an XML Driven Form
Builder. This article takes that concept a step further to discuss how to eliminate the requirement for Form
development not only in an ASP based web application, but also in any potential client of an application. In a
distributed application, various clients are connected to an application server or a server that exposes web
services. These clients can be web browsers, windows applications or handheld devices. Separate programs are
written to display the Forms in the different clients. Instead, these programs can be replaced by generic programs
to build the Forms on-the-fly from an XML file downloaded from a web server. This XML file will define the
platform-neutral Form interface.
With a wider acceptance and implementation of SOAP and Web services, this will be a common scenario in many
applications. Also, as technologies with an Open Access model like the Microsoft 'HailStorm' Services emerge,
where the user has a common information model, the elements like <HailStorm:myAddress> or
<HailStorm:myProfile> will evolve as a natural step. The client applications can build the interfaces as per
the standards of the specific application. Once a framework is in place, the Form designers do not have to do any
recoding - they can simply update the XML file manually, or use a tool built to update the XML. This change will
take effect on all the clients.
Currently XForm is one such standard from the W3C for web based XHTML Forms. You can see some examples of
this at www.mozquito.org.
In this article we will discuss how to build a Form dynamically in a C# based windows application, and discuss
some object oriented patterns as we walk through the example.
http://www.csharptoday.com/content/articles/20010927.asp?WROXE... 2002-07-09
C#Today - Your Just-In-Time Resource for C# Code and Techniques 페이지 2 / 19
Before we jump into our C# specific example, let us see the generic steps that any application trying to build
Forms from an XML file should follow.
z Get the XML file from the database and publish it for the clients.
z The client programs download the XML file from the web server.
z The program parses the XML file and builds the corresponding Form. A web client can use a generic XSLT; the
windows based clients can use the respective DOM or SAX implementations for the language.
z On Form submission, the programs insert the Form data into the database.
Requirements
z .NET SDK Beta 2 This example will not work with the Beta 1 SDK.
z Microsoft SQL Server 7.0 or above. There is a single table used in the application. You can create it in any other
database Server and change the connection string and the SQL queries in the code accordingly.
There are two tables in our example. The first one, tblForm, maintains the Form definition. This data can be fed in
through another utility program, like a generic Form designer. An XML generator program that reads the data
from this table will generate our XML Form definition document.
TblForm
http://www.csharptoday.com/content/articles/20010927.asp?WROXE... 2002-07-09
C#Today - Your Just-In-Time Resource for C# Code and Techniques 페이지 3 / 19
The Form data will be stored into this table after the user submits the dynamically generated Form.
tblFormData
In the code download you will find an SQL script file script.sql, which contains the script to create these
tables. You can run this script in an existing test database or create a new database on the SQL Server. You will
have to provide the appropriate connection string to connect to this database in the MyFormData.cs file that we
shall discuss shortly.
In this article I assume that this XML file is available to us so that we can concentrate on the Form building, rather
than the generation of XML from the database. The following figures show the XML document structure.
If you are using the Visual Studio .NET IDE, you will no longer have to run IE 5.0 or an XML Editor to confirm that
your XML is well formed. The Data View for XML displays error messages similar to the ones that appear in the IE
5.0 Browser. The Data View provides an editable interface to work with the document. This simplifies your task of
manually perusing through the document to check the specific elements.
The Data View of the interface.xml file editor in Visual Studio .NET's built-in editor.
The following figure shows the XML view. This view provides a code editor interface for XML files. An XForm text
field looks like this <xform:textbox />. In our example, the field type is kept in the <type> element. The
http://www.csharptoday.com/content/articles/20010927.asp?WROXE... 2002-07-09
C#Today - Your Just-In-Time Resource for C# Code and Techniques 페이지 4 / 19
<target> element that is used for a potential web client was used in Owen Cutajar's article.
The XML View of the interface.xml file editor in Visual Studio .NET's Built-in Editor.
As discussed above, we will publish this XML file on the web server. So, you will have to copy this file from the
code download to the web root folder (e.g. C:\Inetpub\wwwroot\). We will access this file in our C# Windows
application through the URL " http://localhost/interface.xml ". If you want to keep this file in any other location, you
may have to create a virtual directory and change the URL used in the file Form1.cs file which we shall talk about
later in this article.
Following is an UML class diagram that denotes the class relationships and their individual responsibilities.
http://www.csharptoday.com/content/articles/20010927.asp?WROXE... 2002-07-09
C#Today - Your Just-In-Time Resource for C# Code and Techniques 페이지 5 / 19
The following figure shows the creation of the C# windows application using the VS .NET IDE:
This creates a project with a default file Form1.cs. Rename this file to XMLForm.cs. We shall use this code to
create our abstract base class, XMLForm. This is the class that provides most of the functionality of building the
http://www.csharptoday.com/content/articles/20010927.asp?WROXE... 2002-07-09
C#Today - Your Just-In-Time Resource for C# Code and Techniques 페이지 6 / 19
Windows Form from an XML file. We shall later create the class Form1 that inherits from the XMLForm class, and
provides customization to the Form built by the XMLForm class. The reason behind using the IDE generated file is
that this provides us with a basic template of a Windows Form to work within. The following figure shows the
default code generated by the IDE.
After renaming this file to XMLForm.cs, we shall start modifying this code to suit our requirements. We will add
the classes XMLForm, MyTextBox, MyCheckBox and MyControlfactory, and the interface IMyControl that
are shown in the class diagram above. We shall later create a separate file, MyFormData.cs, for the data access
class MyFormData. After adding the classes and making the modifications to the generated code (as in the last
figure), the XMLForm.cs file looks like this:
using System;
using System.Drawing;
using System.Collections;
using System.Windows.Forms;
using System.Xml;
namespace WindowsFormBuilder
{ //The XMLForm base class
public abstract class XMLForm : System.Windows.Forms.Form
{
protected System.Windows.Forms.Button btnSubmit ;
public XMLForm( int userID , int versionID , string url )
{
InitializeComponent();
}
public override void Dispose()
{
base.Dispose();
}
private void InitializeComponent()
{
//**TO ADD Form Building code here**
http://www.csharptoday.com/content/articles/20010927.asp?WROXE... 2002-07-09
C#Today - Your Just-In-Time Resource for C# Code and Techniques 페이지 7 / 19
}
private void submitButton_Click(object sender, System.EventArgs e){}
}//XMLForm class ends
//***Other classes used by the XMLForm class ***
public class MyControlFactory
{ //******add code for the MyControlFactory class ***
}
public interface IMyControl
{
void InitializeControl(int fieldID ,int userID , string caption ,
string name ,int x_axis ,int y_axis ,int tabIndex);
void Save();
}
public class MyTextBox : System.Windows.Forms.Panel , IMyControl
{ //******add code for the MyTextBox class ***
}
public class MyCheckBox : System.Windows.Forms.Panel , IMyControl
{ //******add code for the MyCheckBox class ***
}
}//WindowsFormBuilder
Now that we have the basic class definitions ready, we shall start adding code to them as I explain each of these
classes one by one. We shall first look at the other classes in the file, and finally look at our main class, XMLForm,
that essentially builds the Form and uses these classes.
The IMyControl interface exposes the two methods InitializeControl() and Save(). These methods will be
called on by the concrete implementations of this interface to initialize the controls and to persist the contained
data to the database respectively.
}
}
}
This class exposes the factory method CreateMyControl() for the creation of the specific controls. It creates
and returns the appropriate control object to the client, or throws an Exception if the type is not supported.
We shall now see how to create the custom controls that provide the implementation for the IMyControl
interface. The main aim behind using these controls is that the Form Builder is able to work with all the controls
added to the Form, as they all belong to a single type, ImyControl, and have a similar behavior that is required
by the Form Builder. This saves a lot of type checking and code in the Form Builder that uses these controls.
http://www.csharptoday.com/content/articles/20010927.asp?WROXE... 2002-07-09
C#Today - Your Just-In-Time Resource for C# Code and Techniques 페이지 8 / 19
MyTextBox Control
This class inherits from the System.Windows.Forms.Panel class and implements the IMyControl interface.
This is a composition of the System.Windows.Forms.TextBox and the System.Windows.Forms.Label
Controls. The custom property Text is exposed for external objects to be able to read or edit the Text property
of the TextBox control.
public void InitializeControl( int fieldID , int userID , string caption , string name
int x_axis ,int y_axis , int tabIndex )
{
this.fieldID = fieldID ;
this.userID = userID ;
this.SuspendLayout();
}
MyCheckBox Control
This class inherits from the System.Windows.Forms.Panel class and implements the IMyControl interface.
This is a composition of the System.Windows.Forms.CheckBox and the System.Windows.Forms.Label
Controls. The custom property Checked is exposed for external objects to be able to read or edit the Checked
property of the CheckBox control. The Database table tblFormData does not have a Boolean field to store the
http://www.csharptoday.com/content/articles/20010927.asp?WROXE... 2002-07-09
C#Today - Your Just-In-Time Resource for C# Code and Techniques 페이지 9 / 19
data, so to accommodate the generic values the save() method converts the boolean value to a string.
public MyCheckBox(){}
public void InitializeControl(int fieldID , int userID , string caption , string name ,
int x_axis ,int y_axis , int tabIndex )
{
int chkXCord = 0;
this.fieldID = fieldID ;
this.userID = userID ;
// Initialize the Panel control.
this.Location = new System.Drawing.Point(x_axis,y_axis );
this.Size = new Size(192 , 40 );
this.SuspendLayout();
The save method in the above two classes uses the CreateField() method of the MyFormData class. This
method saves the Form data provided as arguments into the database tables. We shall shortly see the
http://www.csharptoday.com/content/articles/20010927.asp?WROXE... 2002-07-09
C#Today - Your Just-In-Time Resource for C# Code and Techniques 페이지 10 / 19
implementation of the class MyFormData. The above two custom controls inherit from the
System.Windows.Forms.Panel class. Please note that to create these Custom Controls you can alternatively
have them inherit from the System.Windows.Forms.Control class or the
System.Windows.Forms.UserControl class with no other change in the above code.
This is an abstract base class that inherits from the Form class of the System.Windows.Forms namespace. This
class provides the functionality of downloading the XML interface definition file from the web server, interpreting
it, and building the Form accordingly. The constructor gets called through the constructor of a derived class when
it is instantiated. This constructor in turn calls the InitializeComponent() method, which does the job of the
Form building. The InitializeComponent() method walks through the XML document using the
System.Xml.XmlTextReader class. Before we jump into the code, let us take a brief look at why we chose this
class when we had the other alternative of using the W3C DOM implementation, the System.Xml.XmlDocument
class. Note that there currently is no implementation of the SAX2 standard in the .NET Framework SDK.
Choice of the System.Xml.XmlTextReader Class for walking through the XML document.
The XmlTextReader class offers a forward-only parsing. The .Net platform introduces an alternative method for
parsing an XML document that exposes a pull model, rather than the push model found in SAX. Rather than
loading the entire document into memory, as DOM does, the XmlTextReader class pulls only one node at a time.
The forward-only stream creation means that only one node is in memory at any given time. In our example, we
do not need to insert, update, or delete nodes within the XML document, so using this model will be more efficient
that that of DOM. For more information on the System.Xml namespace read XML in .NET.
The following is the code that goes into the XMLForm class. It is recommended that you simultaneously glance
through the interface.xml document structure as you read through the Form building code, which uses this
XML document.
http://www.csharptoday.com/content/articles/20010927.asp?WROXE... 2002-07-09
C#Today - Your Just-In-Time Resource for C# Code and Techniques 페이지 11 / 19
this.SuspendLayout();
try
{
XmlTextReader xmlReader = new XmlTextReader(url);
While (xmlReader.Read())
{
if (xmlReader.NodeType == XmlNodeType.Element)
{
nodeName = xmlReader.Name;
//keep track of the number of fields added
if( nodeName == 'field' )
{
fieldCount++;
}
if (nodeName == 'frame' && xmlReader.AttributeCount > 0)
{
/*check whether we are processing the frame node that belongs to the
version chosen by the user*/
While (xmlReader.MoveToNextAttribute())
{
if ((xmlReader.LocalName)== 'version' )
{
if( IsValidVersion( Int32.Parse(xmlReader.Value) ) )
{
bValidVersion = true;
}
}
}
}
}
else if(xmlReader.NodeType == XmlNodeType.Text)
{
Switch (nodeName)
{
Case 'type':
/*create the custom control by passing the type as argument to the
Factory method*/
ctrl = MyControlFactory.CreateMyControl(xmlReader.Value);
break;
Case 'background':
//convert HTML color code to System.Drawing.Color
this.BackColor = ColorTranslator.FromHtml( xmlReader.Value );
break;
Case 'title':
//the title in XML becomes the Form title
this.Text = xmlReader.Value ;
break;
Case 'id':
goto case 'caption';
Case 'name':
goto case 'caption';
Case 'caption':
myField[ nodeName ] = xmlReader.Value;
break;
}
}
else if (xmlReader.NodeType == XmlNodeType.EndElement)
{
if( xmlReader.Name == 'field' && bValidVersion )
{
y_cord = y_cord + Y_DIFF;
ctrl.InitializeControl( Int32.Parse( myField[ 'id' ].ToString() )
, userID , myField[ 'caption' ].ToString() ,
myField[ 'name' ].ToString() , x_axis, y_cord ,fieldCount-1 );
this.Controls.Add((System.Windows.Forms.Control) ctrl );
myField.Clear();
}
}
}
http://www.csharptoday.com/content/articles/20010927.asp?WROXE... 2002-07-09
C#Today - Your Just-In-Time Resource for C# Code and Techniques 페이지 12 / 19
}
catch(Exception e )
{
MessageBox.Show('An Exception occured:'+ e.Message );
}
As mentioned earlier, the XmlTextReader maintains only one node in memory as it parses the document with a
forward only cursor. Just like in the SAX model, developers have to create their own data structures for state
management. In documents with complex hierarchies, these data structures can be pretty complex. In our XML
document we have to retain the memory of the one field as we get to the end tag of the field element. This is
realized by the use of a hash table object myField. As the end tag of a field element is reached, the control is
intialized and is added to the controls collection of the Form using the add method of the Form.Controls collection.
The InitializeControl() method is called on every control created by the factory method. The advantage of defining
the IMyControl Interface is more apparent now, as we initialize every control in the same manner irrespective of
its type. The details of initialization are encapsulated into the specific controls. Note that the variable
bValidVersion is a flag indicating whether we are currently parsing the frame element with the right version. In
the above code we are checking for this flag before adding the custom control to the Form. The XML file in our test
application contains only a single frame element. With a greater number of frame elements we would have to
check for this flag at all other places, which I have avoided here to keep it simple.
By this step the Event Handler submitButton_Click has subscribed itself with the button " btnSubmit". On a
click event on the button, this Event handler will be notified so that it does the job of submission of the Form
data to the database.
this.Controls.Add( this.btnSubmit );
this.ResumeLayout(false);
}
This is a simple utility method which checks if the version is a valid one. This is provided as a separate method so
as to accommodate future changes in the logic for validation. You may always return trueif you want to have a
single version.
Visual Studio .NET provides a Windows Form designer test container to test your Form. This Designer adds the
generated code to the InitaializeComponent() method. Although the drag-and-drop Form building process
simplifies development, for special kinds of applications like this one we have to write our own Form building code.
Owing to the dynamic nature of the Form, the Form designer will not be able to load the Form into the design
view, so the only way to test your code will be to run the application.
A glance at the XMLForm.cs file in the design mode will show something like this:
http://www.csharptoday.com/content/articles/20010927.asp?WROXE... 2002-07-09
C#Today - Your Just-In-Time Resource for C# Code and Techniques 페이지 13 / 19
Looks awkward? Wait! We are not done yet. This looks awkward because this is all the designer could interpret out
of the file. The actual Form will be generated when we pass the URL for the XML file, which we will get at after a
few more steps. A word of caution: never try to click or drag-and-drop some control over this Form at this stage.
If you do that, the designer will regenerate the code and you will loose all the code that you manually added so
far.
On clicking the submit button, the observer is notified and the method submitButton_Click is invoked. The
IEnumerator interface supports simple iteration over a collection. An Iterator provides a way to access the
elements of an aggregate object (the concrete implementation of the IMyControl interface in this case)
sequentially without exposing its underlying representation.
We can see the polymorphic behavior in the above code. The save() method is called on every element in the
controls collection. The concrete implementation of the IMyControl Interface, like the MyTextBox and the
MyCheckBox, have the best knowledge of their internal class representation and have been provided with the
behavior of how to save themselves to the database. For instance the MyCheckBox class checks for a Boolean
value of checked status and converts it to a string as "YES" or "NO".
We shall look at creating the class MyFormData that handles the database access. We have maintained it in a
http://www.csharptoday.com/content/articles/20010927.asp?WROXE... 2002-07-09
C#Today - Your Just-In-Time Resource for C# Code and Techniques 페이지 14 / 19
separate .cs file so as to separate the data access layer from the presentation. This class exposes a method
CreateField() which is called from the save method of the custom controls. This method saves the Form data
to the database table tblFormData. From the Solution Explorer, add a new Item as shown below:
using System;
using System.Data;
using System.Data.OleDb;
namespace WindowsFormBuilder
{
The static constructor makes the connection object available when the MyFormData class is first referenced.
http://www.csharptoday.com/content/articles/20010927.asp?WROXE... 2002-07-09
C#Today - Your Just-In-Time Resource for C# Code and Techniques 페이지 15 / 19
{
throw new System.Exception("The Field could not be saved into the Data Store.");
}
}
A Connection pool is used in multi-user applications, like a web application, where connection is a valuable
resource, and releasing and establishing connections for every request would greatly reduce the system
performance. When you use the OLEDB .NET Data Provider, you do not need to enable connection pooling because
the provider manages this automatically. In Windows based single user applications, developers develop their
modules separately, and when the modules are integrated it comes to our notice that more than one simultaneous
connection object is created. Using a singleton pattern for the creation of the connection allows us to instantiate
and maintain a single connection object. The singleton design pattern ensures that a class only has one instance,
and provides a global point of access to it. The connection should be obtained through a single method called
GetConnection() all through the application. A call to the close method releases the connection to the
connection pool, or closes the connection if the connection pooling is disabled. A call to the static method
OleDbConnection.ReleaseObjectPool() indicates that the connection pooling can be cleared when the last
underlying OLE DB Provider is released.
Extending the framework and Customizing the Form in the derived Form1 class
Now that we have created the base class XMLForm, and the data access class MyFormData, let us do the final
step to get our application running. We shall add a Windows Form as shown below:
http://www.csharptoday.com/content/articles/20010927.asp?WROXE... 2002-07-09
C#Today - Your Just-In-Time Resource for C# Code and Techniques 페이지 16 / 19
With the new file Form1.cs added, the Solution Explorer looks as below:
using System;
using System.Windows.Forms;
using System.Drawing;
using WindowsFormBuilder;
[STAThread]
static void Main()
{
Application.Run(new Form1("C# XML Form Builder",1 , 1 , "http://localhost/interfac
}
}
We saw how we can provide customizations to the base class, like changing the background color, the font and
the size for example. The button btnSubmit has a protected access modifier allowing the derived classes to
customize it. The classes can override the event handler, and provide a different OnClick Event handler to
provide a different way of saving the Form data. This may be used to save to another XML file instead of saving to
the database for example. Now that all the pieces are in place, we can build the project and run it to see the
results.
The following figure shows how the generated Form looks after applying the customizations in the derived class.
http://www.csharptoday.com/content/articles/20010927.asp?WROXE... 2002-07-09
C#Today - Your Just-In-Time Resource for C# Code and Techniques 페이지 17 / 19
You can enter the relevant data into the Form and save it to the database by clicking the submit button. That
completes our building and using of the XML Form.
If you wish to try generating some extra stuff on-the-fly, and you are new to the System.Windows.Forms
assembly, or you have been using only the drag-and-drop feature of the Form designer, here is a small short-cut
to make your job simpler. You can learn from the IDE code generator. First, use the drag-and-drop feature to
build a static Form, keeping in mind the Form that you want to be generated dynamically, and then study the code
that is generated. Then try to get that code into some conditional statements. This kind of framework is more
familiar to a Visual Basic Windows application developer, where he is made to think in terms of the event-handlers
rather then the nuances of the Form building. But here (both in C# as well as VB .NET, as they share the .NET
Framework SDK) as long as you wish to do so, you are free to modify the code to suit your own Form building
logic.
Changes from the .NET SDK Beta1 to .NET SDK Beta2 used in the code
You will notice the following changes from Beta1 to Beta2 that I have used in the code. To find more about other
changes go to Change Documents.
We implemented the XML Form builder in C#, now what if you require the same functionality in a Visual
Basic .NET Form? Let us see how we make use of the above code to achieve the same in Visual Basic .NET,
without having to re-invent the wheel. All you need to do is to convert the "WindowsFormBuilder" namespace
into a .NET Class Library and you are ready with a Base class for the both the .NET languages. The first step is to
create a new Class Library. This creates a project with the default file Class1.cs. Delete this file and add the two
files XMLForm.cs and MyFormData.cs to the project, and then build the project. This get you ready with the
WindowsFormBuilder.dll. The next step is to create a Visual Basic .NET Windows Application. Add a reference
to the component file WindowsFormBuilder.dll that you just created by using the browser button.
Delete all code in the auto-generated file Form1.vb and cut and paste the code below into this file.
http://www.csharptoday.com/content/articles/20010927.asp?WROXE... 2002-07-09
C#Today - Your Just-In-Time Resource for C# Code and Techniques 페이지 18 / 19
Inherits WindowsFormBuilder.XMLForm
Build and run the application and you will see the Form generated without any added customizations. Being a VB
middleware COM developer, although I've not worked much on Visual Basic Windows Applications, my experience
with Java helped me create a Windows Form on-the-fly from XML within a minute. You can find the Class Library
"WindowsFormBuilder.dll"; in the code download, and try out the Visual Basic .NET Form inheritance.
However, the data access class will not work unless you have the same connection string as in the code download.
So, to get the fully working version, it is recommended that you follow the above steps to create your own Class
Library. A good introduction to .NET Windows Form Inheritance can be found at
http://www.asptoday.com/content/articles/20010424.asp
Inheritance or Aggregation?
Whether to inherit or to aggregate has always been a point of discussion amongst Object Oriented Designers. So,
was our approach of specialization from the base class XMLForm right? As long as we aim towards using an XML
based dynamic Form generation, we are on the right track. In the future we could be adding different classes to
the WindowsFormBuilder namespace, providing different implementations like "database" driven Form building,
building Forms from a serialized Form Object, or a "static" Form. So, if we want to have the flexibility at run time
to change the implementation, we will find that aggregation will be a better solution.
z Develop a web or Windows based tool to define the Form and store it in the database.
z If we were to develop a Web Service for serving the interface definition file, there would be no need to download
the entire XML document from the web server if the XML is cached on the client and we could invoke a SOAP
method with the last cached version or Time-Stamp. In response, the client would receive an indication to use
the cached file or a single 'frame' element corresponding to the latest version of the Form that could be returned
by the Web Service.
z The Windows client is assumed to be on the same network as the database server. This might not always be
case; they can be across the Internet. So building a Web service would be an ideal step.
z Add an isValid() method to the IMyControl interface, and provide implementation for the field validations.
z Building Forms on the fly will have performance limitations over Forms containing design time controls. The
trade-offs between flexibility and performance have to be considered by the designers.
In this article we saw how to build a .Net Windows Form dynamically from an XML file. I shall discuss Building
Dynamic Interfaces with XML and ASP.NET server controls in a future article.
http://www.csharptoday.com/content/articles/20010927.asp?WROXE... 2002-07-09
C#Today - Your Just-In-Time Resource for C# Code and Techniques 페이지 19 / 19
C#Today is brought to you by Wrox Press (www.wrox.com). Please see our terms and conditions and privacy
C#Today is optimised for Microsoft Internet Explorer 5 browsers.
Please report any website problems to [email protected]. Copyright © 2002 Wrox Press. All Rights
http://www.csharptoday.com/content/articles/20010927.asp?WROXE... 2002-07-09