22Nov 2019

Building Web Forms in C# (Update and Search Operation)

In the previous article, we explained how to build a form generator in C# so that forms could be dynamically generated from any object and in a recursive way.

We built so far the Create and Read functions but we still need to build the Update operation and finally, we will build a Search function.

Implementation

Generate the “Update” Form from an Object 

generate update form from an object

The Update function needs to be able to “marshalize” a generated form into an object.

In fact, with the Create function we “only” dynamically generated a form that has the same field as an object, but if we want to really Create not only the form – but also the object from the created form – we need as well a Marshall function. Here we consider that saving a generated form into an object is an “update” mechanism whatever the object existed or not.

We already know how to generate a form and how to populate it from the objects values. What we need now is a way to convert a form into an object.

/// <summary>
/// 
/// Take a populated form - as generated by the generator - and returns the corresponding object
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="controls"></param>
/// <returns></returns>
public static T Form2Object<T>( Control[] controls) where T: new()
       {

First, we need to create a new object from the type T.

T obj2 = new T();

(hence the constraint on the generic type T) 

Then we will simply call

obj2=FillValueIntoObjFromControl<T>(obj2, control);

The function FillValueIntoObjFromControl will do the exact opposite of FillValueIntoControlFromObj.

As usual, we parse the control ID and we try to find the required field in the object.

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

string v = ((TextBox)control).Text;

string n = data[1];

if (!Utils.setFieldValue(t,n, obj2, v))
throw new IncorrectFormGeneratorException("Cannot set Value =" + v + "  for field=" + n);
}

We get setFieldValue this is the opposite of getFieldValue.

public static bool setFieldValue(Type t,string nameField, object obj, object Value)
{

 FieldInfo[] fields = t.GetFields();

foreach (FieldInfo info in fields)
{
                    if (info.Name == nameField)
                    {
                        info.SetValue(obj, Value);
                        return true;
                    }
}
return false;

This consists mainly of calling SetValue from the right FieldInfo.

That’s mainly it! Of course, we created validators for the inputs. The system will work great for ASP.NET controls.

Generate the “Search” Form from an Object

generate search form from an object

Finally, we need to generate search forms and be to generate the search criterion from the form. The search forms are a bit different because they require special ranges for integers, floats and numerical values in general. The textboxes will stay the same and we will allow selecting only one value into the enums so that only the numerical values will require two text Boxes: min and max.

As an example, we display the code for retrieving the checkbox values and convert them into a search parameter. We will generate a search form with checkboxes to enable such or such parameter.

We first need to check if the search parameter has been enabled. For this we code additionally this info into the control ID as such:

SEARCHFORM_ENABLE#BOOL#BOOL%BOOLEAN_FIELD_NAME

We first generate a form with the right controls.

//we generate an insert form +
//for each float there is a max and min float form
//for each int there is a max and min int

//for each string there is a pattern with regexp
//for each enum or bool, one can choose a value
//objects like dates or GUID are handled separately
// for each field a tick box allow to enable/disable the search field

List<System.Collections.Generic.KeyValuePair<String, Control>> controls = generateInsertForm<T>();

This form will have checkboxes associated with each control and numerical values will have two controls (max and min) instead of just one. Once the form has been populated we need to convert it into a list of matching objects.
The constraint about

This form will have checkboxes associated with each control and numerical values will have two controls (max and min) instead of just one. Once the form has been populated we need to convert it into a list of matching objects.
The constraint about where T : dataVaultStorage is because we also are using a repository object to search the objects but this does not impact the code here.

  is because we also are using a repository object to search the objects but this does not impact the code here.

/// <summary>
/// 
/// convert a list of search controls into a list of objects matching the search criteria
/// 
/// </summary>
/// <typeparam name="T">the type to search</typeparam>
/// <param name="controls">the controls containing the data range to search</param>
/// <returns>a list of matching objects</returns>

public static List<T> searchFormToObjectList<T>(Control[] controls, List<T> list_= null) where T : dataVaultStorage , new()
    {

We get all the values and for each criterion, we look at how many objects from the input list satisfy the search parameter. We start with the full list of an object then we filter iteratively for each criterion.

For instance:

else if (type.ToUpper() == "INT")
{
Control c_ref_min = controls.First(r => r.ID == "SEARCHFORM_MIN#" + id);

Control c_ref_max = controls.First(r => r.ID == "SEARCHFORM_MAX#" + id);

if ((c_ref_min == null)|| (c_ref_max == null))
throw new IncorrectFormGeneratorException("incorrect id" + id);

if ((c_ref_min.GetType() != typeof(TextBox))&amp;&amp;(c_ref_max.GetType() != typeof(TextBox)))
throw new IncorrectFormGeneratorException("incorrect id" + id);

string res1_min = ((TextBox)c_ref_min).Text;
string res2_max = ((TextBox)c_ref_max).Text;

int min = 0;
int max = 0;
int.TryParse(res1_min, out min);
int.TryParse(res2_max, out max);

TextBox textbox = new TextBox();
textbox.ID = id;

foreach (T obj in list_all)
{
Control c1 = FillValueIntoControlFromObj(typeof(T), obj, textbox);

if (c1 == null)
continue;

if (c1.GetType() != typeof(TextBox))
throw new IncorrectFormGeneratorException("incorrect id" + id);
textbox = (TextBox)c1;

string res = textbox.Text;
int ires = 0;

int.TryParse(res, out ires);

if (!((ires>max)||(ires<min)))
list.Add(obj);
}
}

Once all criteria have been processed the final list represents the matching objects.

We finally have everything ready. We can start to test our form generator!

Using the Form Generator

We will create an ASP.NET project and dynamically generate the 4 forms using our form generator.

We create then a test.aspx web page, we will use it to host the generate forms.

We have access to the generator functions and we will insert the control list from the code behind.

We simply call generateInsertForm() function in code behind and we format the output:

protected void Page_Load(object sender, EventArgs e)
        {


List<KeyValuePair<String, Control>> list = Generator.GenerateForms.generateInsertForm<Generator.GenerateForms.Tracer.Laptop>();

foreach(KeyValuePair<String, Control> val in list)
            {
                if (!val.Key.Equals(""))
                {
Literal html = new Literal();

html.Text = "<TR><TD bgcolor=\"#000000\" align=\"center\"><font color=\"#FFFFFF\">" + val.Key+ "</font></TD><TD>";
                    form1.Controls.Add(html);

                    form1.Controls.Add(val.Value);

                    html = new Literal();
                    html.Text =   "</TD></TR>";
                    form1.Controls.Add(html);
                }
            }
        }

As a result, we get a form generated from our Laptop object.

If we want to generate a Read form, eg a form populated with the value of an existing Laptop object

We create a new laptop object

Generator.GenerateForms.Tracer.Laptop laptop = new Generator.GenerateForms.Tracer.Laptop();
laptop.brand_ = Generator.GenerateForms.Tracer.brands.Acer;
laptop.computer_ID = Guid.NewGuid().ToString();
laptop.is_new_ = true;
laptop.price_ = (float)270.56;
laptop.quantity_ = 10;
laptop.shop_ = new Generator.GenerateForms.Tracer.shop();
laptop.shop_.name_ = "Lakshmi's laptops";
laptop.shop_.street_address_ = "B-2, 1st Floor, 7, Mc Nichols Road, Chetpet ";
laptop.shop_.town = Generator.GenerateForms.Tracer.towns.Delhi;
laptop.shop_.ZIP_ = 400008;

We simply need to callFillValueIntoControlFromObj once the controls have been created.   We factor the generation into a dedicated function:

void GenerateForm(Generator.GenerateForms.Tracer.Laptop laptop)
        {
...

form1.Controls.Add(val.Value);
               Generator.GenerateForms.FillValueIntoControlFromObj(typeof(Generator.GenerateForms.Tracer.Laptop), laptop, val.Value);
...

And we get the read form.

We also test the Update function. For this, we will have simply to call the Form2Object function with the list of controls from the form as an argument.

Generator.GenerateForms.Tracer.Laptop laptop2 =Generator.GenerateForms.Form2Object<Generator.GenerateForms.Tracer.Laptop>(list.ToArray());

We will modify the initial form, then save it using a “save” button and read it again by calling the GenerateForm function. we will modify the price, quantity and change computer status to “second hand”.

We need to test the Search form generation. We use the same code to generate the HTML but instead, we call

List<KeyValuePair<String, Control>> list = Generator.GenerateForms.generateSearchForm<Generator.GenerateForms.Tracer.Laptop>();

We check that the right search form is generated:

 We will test a search within a list of Laptop objects.

List<Laptop> list_laptops = new List<Laptop>();

Generator.GenerateForms.Tracer.Laptop laptop = new Generator.GenerateForms.Tracer.Laptop();
laptop.brand_ = Generator.GenerateForms.Tracer.brands.Acer;
laptop.computer_ID = Guid.NewGuid().ToString();
laptop.is_new_ = true;
laptop.price_ = (float)270.56;
laptop.quantity_ = 10;
laptop.shop_ = new Generator.GenerateForms.Tracer.shop();
laptop.shop_.name_ = "Lakshmi's laptops";
laptop.shop_.street_address_ = "B-2, 1st Floor, 7, Mc Nichols Road, Chetpet ";
laptop.shop_.town = Generator.GenerateForms.Tracer.towns.Mumbai;
laptop.shop_.ZIP_ = 400008;

list_laptops.Add(laptop);

laptop.brand_ = Generator.GenerateForms.Tracer.brands.Asus;
laptop.computer_ID = Guid.NewGuid().ToString();
laptop.is_new_ = true;
laptop.price_ = (float)500;
laptop.quantity_ = 10;
laptop.shop_ = new Generator.GenerateForms.Tracer.shop();
laptop.shop_.name_ = "Lakshmi's laptops";
laptop.shop_.street_address_ = "B-2, 1st Floor, 7, Mc Nichols Road, Chetpet ";
laptop.shop_.town = Generator.GenerateForms.Tracer.towns.Mumbai;
laptop.shop_.ZIP_ = 400008;

list_laptops.Add(laptop);

laptop.brand_ = Generator.GenerateForms.Tracer.brands.Acer;
laptop.computer_ID = Guid.NewGuid().ToString();
laptop.is_new_ = true;
laptop.price_ = (float)950;
laptop.quantity_ = 10;
laptop.shop_ = new Generator.GenerateForms.Tracer.shop();
laptop.shop_.name_ = "Lakshmi's laptops";
laptop.shop_.street_address_ = "B-2, 1st Floor, 7, Mc Nichols Road, Chetpet ";
laptop.shop_.town = Generator.GenerateForms.Tracer.towns.Mumbai;
laptop.shop_.ZIP_ = 400008;

list_laptops.Add(laptop);

List<Control> list = new List<Control>();

            foreach (Control c in form1.Controls)
            {
                list.Add(c);
            }
            Generator.GenerateForms.searchFormToObjectList<Laptop>(list.ToArray(), list_laptops);

        }

We will print the matching laptops with the following code.

List<Laptop> list2=  Generator.GenerateForms.searchFormToObjectList<Laptop>(list.ToArray(), list_laptops);

            Literal html = new Literal();
            html.Text = "<h3>Search results:</h3>";
            form1.Controls.Add(html);

            foreach (Laptop laptop_ in list2)
            {
                GenerateForm(laptop_);

            }

Additional Resources

The Form Generator is a simple but efficient tool to generate without effort any ASP.NET form from any C# object. The complete source code is available here. It can be used for any purpose and without restriction.

The code can be obviously improved and should be refactored for example to make the recursion most efficient.

Acodez is an award-winning web design and software development company in India. Our reliable web design and web development services to our customers using the latest technologies. We are also a leading digital marketing agency providing SEO, SMM, SEM, Inbound marketing services, etc at affordable prices. For more details, 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 *