Monday, October 18, 2010

Working with Dynamic Data Controls: Finding, Set Value, Get Value

Once you start working with ASP.NET, FormView, and the DynamicData controls you will soon learn that it isn’t as convenient to get and set values as you might like. You may also want to find controls not by their name, but by the column / property that they are bound to in the ADO.NET Entity Model.

I have put together some extensions to the Control class that make doing all these things much easier. All you have to do is create a class in your project called ControlExtensions. Copy and paste the code below into it. Change the namespace as needed. Then on the page you want to use it, just include the namespace. Then when you look at the methods on any control you will see the methods below. I have tested the code below with a FormView and also with DynamicControls on a User Control  that is then on a FormView. In both cases, the code works well.

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.DynamicData;
using System.ComponentModel;

namespace DistiPromo.Helpers
{
    public static class ControlExtensions
    {

        /// <summary>
        /// This will find the Dynamic Data control for the specified column (name from Entity model)
        /// </summary>
        /// <param name="control">the control (such as FormView or UserControl) that directly contains the control you are looking for</param>
        /// <param name="columnName">the name of the column (from the Entity Model) that you are looking for</param>
        /// <returns>The DynamicData Control for the column that you are looking for</returns>
        public static Control FindFieldTemplate(this Control control, string columnName)
        {
            // code copied from internal method: System.Web.DynamicData.DynamicControl.GetControlIDFromColumnName
            // Since it is internal, it could change, but I needed to get to it
            return control.FindControl("__" + columnName);
        }

        public static FieldTemplateUserControl FindFieldTemplateUserControl(this Control control, string columnName)
        {
            var userControl = ((FieldTemplateUserControl)(control.FindFieldTemplate(columnName)));
            if (userControl == null) throw new Exception("Could not find FieldTemplate in Control for " + columnName);
            return userControl;
        }

        public static Control FindControlForColumn(this Control control1, string columnName)
        {
            var control = FindFieldTemplateUserControl(control1, columnName).DataControl;
            if (control == null) throw new Exception("Could not find control in Control for " + columnName);
            return control;
        }

        public static T FindControlForColumn<T>(this Control control, string columnName) where T : Control
        {
            return FindControlForColumn(control, columnName) as T;
        }

        public static object GetValueForColumn(this Control control, string columnName)
        {
            var actualControl = FindFieldTemplateUserControl(control, columnName).DataControl;
            return GetValue(control, actualControl);
        }

        public static T GetValue<T>(this Control control, string columnName)
        {
            object rawVal = GetValueForColumn(control, columnName);
            //return (T)Convert.ChangeType(rawVal, typeof(T));

            // this is a little better because it handles nullable types also
            return (T)TypeDescriptor.GetConverter(typeof(T)).ConvertFrom(rawVal);
        }

        public static T GetValue<T>(this Control control1, Control control)
        {
            object rawVal = GetValue(control1, control);
            //return (T)Convert.ChangeType(rawVal, typeof(T));

            // this is a little better because it handles nullable types also
            return (T)TypeDescriptor.GetConverter(typeof(T)).ConvertFrom(rawVal);
        }

        public static object GetValue(this Control control, Control actualControl)
        {
            if (actualControl is ITextControl)
            {
                return (actualControl as ITextControl).Text;
            }
            else if (actualControl is ICheckBoxControl)
            {
                return (actualControl as ICheckBoxControl).Checked;
            }
            else if (actualControl is ListControl)
            {
                return (actualControl as ListControl).SelectedValue;
            }
            else if (actualControl is HiddenField)
            {
                return (actualControl as HiddenField).Value;
            }
            else throw new Exception("Could not get value of unknown control type: " + actualControl.GetType().ToString());

        }

        public static void SetValue(this Control control, string columnName, object value)
        {
            Control actualControl = FindControlForColumn(control, columnName);
            SetValue(control, actualControl, value);
        }

        public static void SetValue(this Control control, Control actualControl, object value)
        {

            if (actualControl is ITextControl)
            {
                (actualControl as ITextControl).Text = Convert.ToString(value);
            }
            else if (actualControl is ICheckBoxControl)
            {
                (actualControl as ICheckBoxControl).Checked = Convert.ToBoolean(value);
            }
            else if (actualControl is ListControl)
            {
                (actualControl as ListControl).SelectedValue = Convert.ToString(value);
            }
            else if (actualControl is HiddenField)
            {
                (actualControl as HiddenField).Value = Convert.ToString(value);
            }
            else throw new Exception("Could not set value of unknown control type: " + actualControl.GetType().ToString());
        }
    }
}

5 comments:

electronic signature said...

The code you provided really made the life easy as you said that I need to save it once and wherever I need it i just need to include the namespace and that is all and all the methods will be available that is so handy and also keeps the code cleaner

Brent V said...

I'm so glad you found it useful. I totally agree. I love how it makes everything much cleaner.

-Brent

Jasmine said...

Could you help on how to use it. I have created your class but how do I say see what field is being updated on update. I went to Edit.aspx.cs and called one of your methods but it errors.

Brent V said...

Hi Jasmine,

You can use this method in many places. I suppose you could use it in Edit.aspx.cs. I have used in in Field templates, user controls that are on the entity template, formview, and on entity templates. For example, you could call these methods on the formview. You can call them on a event handlers such as selectionChanged, as well as PreRender, button clicks, etc.

Basically you use these methods anywhere you would use the code that they call. There is nothing special about the code that I have written, you still have to know when in the page lifecycle to call these methods. Look at the code in the method you are trying to call. Be sure you are not calling it too early in the lifecycle.

I can better assist you if you have an example of what method you are calling, the error you are getting, and the place you are calling it from, as well as what you do to get the error.

I hope this helps,

Brent

Claudio said...

Hi Brent. Your code can be very usefull for my project, but i'm starting in dynamic data and i don't know how to call your code in the edit.aspx to find out the id value. can you give some example? thanks in advance :)