22Nov 2019

How to Build a Form Generator in C# (C-Sharp)?

In this article, we will see how to build a custom form generator in C#. The techniques fully involve the reflection package.

Description of the Problem

Description of the problem

We wish to build automatically forms from an object. We wish for example that a form be created automatically with the various (public) fields from an object which may contain fields such as text, integers, enums or even structures or subclasses. The generator may be used for CRUD operations, creating, reading and updating and additionally searching.

The project is not so simple because we will need to detect from an object the various fields and properties it contains and their natures, then associate a dedicated control to each field or properties. For instance:

TextBox ⇒String,Integers;
Checkbox ⇒Boolean;
ComboBox ⇒Enum.

Of course, the generator must be able to recursively go through the fields, meaning if the field is a structure, it must parse it in order to detect the fields that the structure contains, etc…

Additionally, the form must be able to do validation of the input based on the constraints of the represented field. For example, controlling the value is numerical if the control is  TextBox representing an integer.

We will be using the generator in a web context, namely for ASP.NET forms but it can be easily adapted for winforms.

 We wish to be able to generate a form with the right controls and labeled by the name of the fields of the object.  The generation can be for reading, then the controls are read-only and populated with the value(s) of the object fields. If the generation is for the creation or updating the controls can be edited. If the generation is for search, a range of values can be selected in order to generate the search criteria.

The interest of such a generator may seem small in case there are but a few objects with few fields but imagine when there are hundreds of different objects with hundreds of various fields inside! 

To illustrate, let us suppose that we have an object – a class – named Laptop defined as such:

Public class Laptop
{
Public String computer_ID;
Public shop shop_;
Public int quantity_;
Public float price_;
Public brands brand_; 
Public Boolean is_new_; 
}

Where shop is a structure:

Public struct shop
{
Public String name_;
Public String street_address_;
Public Int32 ZIP_;
Public towns town;
}

and brands, towns are two enums.

Public enum brands
{
Lenovo=0,
Hp=1,
Acer=2,
Asus=3
}

Public enum towns
{
Mumbai=0,
Delhi=0,
Bangalore=0,
...
}

We want to automatically generate Create, Read, Update and Search forms from that object.

Implementation

Generate a New Form From an Object (“Create”)

create a new form

We need to get a list of the fields and properties of the object.

// public fields
foreach(FieldInfo fieldInfo in type.GetFields()) 
{

And for the list of properties:

// public properties
foreach (PropertyInfo propertyInfo in type.GetProperties())
        {

The type of the object is obtained by using a parameter generic type field <T> in the generator function’s prototypes :

public static List<System.Collections.Generic.KeyValuePair<String, Control>> generateInsertForm<T>(/*Type type*/)
    {

Type type = typeof(T);

We will restrict ourselves to public fields

if (fieldInfo.IsPublic)
            {

We then need to detect the type of the object fields

if (fieldInfo.FieldType == typeof(String))
                {
…
}
else if (fieldInfo.FieldType.IsEnum)
{
…
}
else if (fieldInfo.FieldType == typeof(float))
                {
…
}

We process integers, float, booleans, and finally, we detect fields which are classes or structures.

else if (fieldInfo.FieldType.IsValueType)
                {
…
}

Next, we have to generate the right control for each type of input:

TextBox textbox = new TextBox();
textbox.ID = "STRING%" + fieldInfo.Name;
textbox.Height = 30;
textbox.Width = 300;
textbox.Font.Bold = true;
textbox.Font.Size = new FontUnit(FontSize.Large);

if (fieldInfo.Name.ToUpper() == "INFO")
{
textbox.Text = "newly created object";
textbox.Enabled = false;
  }

RegularExpressionValidator regex = new RegularExpressionValidator();
regex.ValidationExpression = "[a-zA-Z0-9][a-zA-Z0-9-_'.+/=\\s][a-zA-Z0-9-_'./=+\\s]*";
regex.ID = "RegularExpressionValidator_" + textbox.ID.Replace('%', '_').Replace('.', '_').Replace('+', '_').Replace('@', '_');
regex.ControlToValidate = textbox.ID;
regex.ErrorMessage = "alphanumeric value( including spaces,dots _'/=+), min 2 characters";
                     
list.Add( new KeyValuePair<string,Control>(fieldInfo.Name, textbox));
//   list.Add( new KeyValuePair<string,Control>("validator_" + fieldInfo.Name, regex));
list.Add(new KeyValuePair<string, Control>("", regex));

We store the type in the control ID:

textbox.ID = "STRING%" + fieldInfo.Name;

We add validators so the input contains only alphanumeric value and a set of permitted extra-characters, then we add the control in a list. The generator indeed returns a List<System.Collections.Generic.KeyValuePair<String, Control>> object which contains all the controls for the generated form.

We will do roughly the same for all the other types except the structures or classes.

For these values, we will have to loop again:

foreach (var field in fieldInfo.FieldType.GetFields())
{
if (field.IsPublic)
                        {
if (field.FieldType == typeof(String))
                        {

In these cases, the ID of the generated control will have to tell the nature and name of the structure or class so it will look slightly more tokenized:

TextBox textbox = new TextBox();
textbox.ID="STRUCT%" + fieldInfo.FieldType.FullName +"@@"+fieldInfo.Name + "@@STRING%" + field.Name;

This will allow the form to be parsed and the controls re-created.

As a matter of fact, we have created a serializer that converts objects into forms.

A different approach would have consisted of serializing the object using an XML serializer fort then parsing the XML to get the fields. Here we develop the whole logic from scratch!

Generate a “Read” Form From an Object 

Read form from an object

So far we have developed the Creation function, which generates a brand new form from an object. The Read function will use the same principles but we need a way to populate our controls with the value of the fields. We could dynamically generate the fields from the type using a generic type field but to populate them we need a way to.

public static Control FillValueIntoControlFromObj(Type t,object obj2,Control control)
    {

The function will parse the control ID, retrieve its type and name then get the corresponding value from the object.

if(id.StartsWith("STRING%"))
{
//get the field
String[] data = id.Split(new char[] { '%' });
String h = data[1];

//fill field 'h' of object with string value from control
String val=(String)Utils.getFieldValue(t, h, obj2);

if ((control.GetType() != typeof(TextBox)))
throw new IncorrectFormGeneratorException("incorrect format from the Form generator:" + id + " " + control.GetType());

((TextBox)control).Text = val;
 return control;
}

The function Utils.getFieldValue is retrieving the value from the object as such:

public static object getFieldValue(Type t,string nameField, object obj)
            {
                FieldInfo[] fields = t.GetFields();
                foreach (FieldInfo info in fields)
                {
                    if (info.Name == nameField)
                    {
                        return info.GetValue(obj);
                    }
                }
                return null;
            }
}

We go through all the FieldInfo values of the object and when we find a matching name, we simply invoke the GetValue function from the FieldInfo.

We do the same for “BOOL%”, “INT%”, “FLOAT%”, etc…

For enums it is a bit more complicated because we need to retrieve the enum full name :

else if (id.StartsWith("ENUM%"))
            {

String[] data = id.Split(new char[] { '%' });

String h = data[1];
//ENUM%enum_name@@object_type_name
//get the enum fullname
if(!h.Contains("@@"))
throw new IncorrectFormGeneratorException("incorrect format from the Form generator:" + id + " " + control.GetType());

String[] data2 = h.Split(new String[] { "@@" },StringSplitOptions.None);

string fn = data2[0];

//get the field
String f = data2[1];
String val = Utils.getFieldValue(t, f, obj2).ToString();

if ((control.GetType() != typeof(DropDownList)))
throw new IncorrectFormGeneratorException("incorrect format from the Form generator:" + id + " " + control.GetType());
((DropDownList)control).SelectedValue =  val;
return control;
            }

For the structure, we need to parse the ID which looks like

STRUCT%struct_type@@struct_name@@struct_field_type%struct_field_name

We choose such a format to mangle the object information into the ID.

For instance, with our Laptop example this would look like:

STRUCT%shop@@shop_@@String%street_address

First, we get the structure object:

//the structure object (shop_)
object  o= Utils.getFieldValue(t, n, obj2);

Then we call again FillValueIntoControlFromObj but it will be a recursive call: the function will loop through all elements of the structure and if it detects others’ structure it will iterate and so on…

//for each of its fields, we need to loop and populate the control
Assembly assembly = Assembly.GetCallingAssembly();

//fn = name of the structure (shop)
Type t2 = assembly.GetType(fn);

string oldid = control.ID;
control.ID = newid;
Control d=  FillValueIntoControlFromObj(t2, o, control);

if (d == null)
 return null;

d.ID=oldid;
 return d;
...

We are able to build the Read operation, we still need to build the update operation, This will be explained in the second part of that article.

Check out how to implement the update and search functions in the form generator tool.

Acodez is a leading website design and software development company in India. We offer all kinds of web design and web development services to our clients using the latest technologies. We are also a leading digital marketing agency providing SEO, SMM, SEM, Inbound marketing services, etc at affordable prices. For further information, please contact us.

Looking for a good team
for your next project?

Contact us and we'll give you a preliminary free consultation
on the web & mobile strategy that'd suit your needs best.

Contact Us Now!
Jamsheer K

Jamsheer K

Jamsheer K, is the Tech Lead at Acodez. With his rich and hands-on experience in various technologies, his writing normally comes from his research and experience in mobile & web application development niche.

Get a free quote!

Brief us your requirements & let's connect

Leave a Comment

Your email address will not be published. Required fields are marked *