Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Model Binder in ASP.NET webforms

For number of years I did ASP.NET web forms development I was spoiled by a proprietary library, which allowed me to do things like:

    UpdateToObject(ControlsCollection, obj)
    UpdateFromObject(ControlsCollection, obj)

Conceptually code did something very similar to what MVC Model Binder does, i.e. given form's posted values as input it would populate custom object. Basically it freed developer from doing monkey code such as

employee.Name = txtName.Text;
employee.DOB = DateTime.Parse(txtDOB.Text);

and so on..

Now, this proprietary library is not available on the new project I'm involved with, and it's a Web forms project. So I'm wondering if there is a way of using System.Web.Mvc.DefaultModelBinder in the context of Web forms. Goal is to achieve simple and easy population of controls from domain objects and back, ideally with validation annotations taken into consideration. If such is not possible could somebody point me to an open source solution to address this need. I really don't feel like rewriting such code.

Thanks in advance.

like image 450
Sherlock Avatar asked Sep 09 '10 20:09

Sherlock


1 Answers

Sherlock, you'll run into some issues trying to use the ModelBinder from MVC since they rely on a ControllerContext.

I answered a similar question earlier ChangeType, Convert - Converting from one type to another but it is really what you're looking for.

Check out this blog post on my blog ChangeType – Changing the type of a variable in C#

Essentially, you get a single method called ChangeType<T> that returns the value of the parameter you're looking for in a strongly typed fashion or a default value if the parameter does not exist.

Now as regards custom classes (DTO type classes mainly), if you don't mind using reflection then I have a solution that will handle most custom classes as well. The DtoBinder class mentioned towards the end of the will job nicely.

Essentially, the last 3 code listings contain all of the code you'll need in order to handle almost every need you have in a typical Web application scenario. Plus it's extensible, so if you need to implement your own binder you can do that very simply and register your binder with the RequestBinder from anywhere in your app.

So if you don't want to use reflection for certain frequently used DTO objects, you can implement a binder for the type and register it and from that point on it will use your custom binder. In many ways it's similar to the MVC ModelBinder in concept.

Edited -

Below is one .cs file with a bunch of classes that I've used in the past to do exactly what you need. The first one MsPropertyAssignerProvider is the one you'd be working with from within your page.

You'd iterate over your controls and call the GetPropertyAssigner method passing it the type name of the control. This method returns an instance of an ObjectPropertyAssigner that has one method called SetPropertyValue that you can pass your object instance and the control instance to.

  internal class MsPropertyAssignerProvider
  {
    private Hashtable propertyAssigners;

    internal MsPropertyAssignerProvider()
    {
      propertyAssigners = new Hashtable();
      RegisterPropertyAssigner(typeof(TextBox).ToString(), new TextBoxValueExtractor());
      RegisterPropertyAssigner(typeof(DropDownList).ToString(), new DropDownListValueExtractor());
      RegisterPropertyAssigner(typeof(Label).ToString(), new LabelValueExtractor());
      RegisterPropertyAssigner(typeof(CheckBox).ToString(), new CheckBoxValueExtractor());
    }

    internal void RegisterPropertyAssigner(string identifier, IMsObjectPropertyAssigner assigner)
    {
      if (propertyAssigners.ContainsKey(identifier))
        throw new DuplicatePropertyAssignerRegistrationException(identifier);
      propertyAssigners.Add(identifier, assigner);
    } 

    internal IMsObjectPropertyAssigner GetPropertyAssigner(string identifier)
    {
      return (propertyAssigners.ContainsKey(identifier)) ? (IMsObjectPropertyAssigner)propertyAssigners[identifier] : null;
    }
  }

The accompanying class are listed below

  public interface IMsObjectPropertyAssigner
  {
    void SetPropertyValue(object obj, System.Web.UI.Control control); 
  }

  internal abstract class BaseValueExtractor : IMsObjectPropertyAssigner
  {
    protected MsReflectionHelper reflectionHelper = new MsReflectionHelper();
    protected string FixStringForNumber(string stringValue)
    {
      if (stringValue.Length == 0)
        return "0";
      else
        return stringValue;
    }
    public abstract void SetPropertyValue(object obj, System.Web.UI.Control control);
  }

  internal class TextBoxValueExtractor : BaseValueExtractor
  {
    public override void SetPropertyValue(object obj, System.Web.UI.Control control)
    {
      TextBox textBox = (TextBox)control;
      PropertyInfo propInfo = reflectionHelper.GetPropertyInfo(obj, control.ID);
      Type propType = propInfo.PropertyType;
      if (propType == typeof(System.String))
        reflectionHelper.SetPropertyValue(obj, control.ID, textBox.Text);
      else if (propType == typeof(System.Int16))
        reflectionHelper.SetPropertyValue(obj, control.ID, Int16.Parse(FixStringForNumber(textBox.Text), System.Globalization.NumberStyles.Currency));
      else if (propType == typeof(System.Int32))
        reflectionHelper.SetPropertyValue(obj, control.ID, Int32.Parse(FixStringForNumber(textBox.Text), System.Globalization.NumberStyles.Currency));
      else if (propType == typeof(System.Int64))
        reflectionHelper.SetPropertyValue(obj, control.ID, Int64.Parse(FixStringForNumber(textBox.Text), System.Globalization.NumberStyles.Currency));
      else if (propType == typeof(System.Double))
        reflectionHelper.SetPropertyValue(obj, control.ID, Double.Parse(FixStringForNumber(textBox.Text), System.Globalization.NumberStyles.Currency));
      else if (propType == typeof(System.Single))
        reflectionHelper.SetPropertyValue(obj, control.ID, Single.Parse(FixStringForNumber(textBox.Text), System.Globalization.NumberStyles.Currency));
      else
        reflectionHelper.SetPropertyValue(obj, control.ID, textBox.Text);
    }
  }

  internal class DropDownListValueExtractor : BaseValueExtractor
  {
    public override void SetPropertyValue(object obj, System.Web.UI.Control control)
    {
      DropDownList dropDownList = (DropDownList)control;
      reflectionHelper.SetPropertyValue(obj, control.ID, dropDownList.SelectedValue);
    }
  }

  internal class LabelValueExtractor : BaseValueExtractor
  {
    public override void SetPropertyValue(object obj, Control control)
    {
      Label label = (Label)control;
      reflectionHelper.SetPropertyValue(obj, control.ID, label.Text);
    }
  }

  internal class CheckBoxValueExtractor : BaseValueExtractor
  {
    public override void SetPropertyValue(object obj, Control control)
    {
      CheckBox checkbox = (CheckBox)control;
      reflectionHelper.SetPropertyValue(obj, control.ID, checkbox.Checked);
    }
  }

Sorry no matter what I do the editor completely messes up the code listing. But I hope this helps.

like image 158
Shiv Kumar Avatar answered Sep 29 '22 20:09

Shiv Kumar