asp.netweb-controlsasp.net-webcontrol

How to get value from System.Web.UI.Control (base class)?


Is there a way to access a control's value/text using the base class?

Looking at TextBox and HiddenField, System.Web.UI.Control is the most specific base class they share. Seems like a good candidate, but I don't see an obvious way to access the text/value.

I do see that both class definitions use the ControlValuePropertyAttribute to identify the property that holds their text/value... e.g. HiddenField is set to "Value", TextBox is set to "Text". Not sure how to use that info to retrieve the data, though.

Background

I have an extension method that takes the text of some web control (e.g., a TextBox) and converts it to a given type:

<Extension()> _
Public Function GetTextValue(Of resultType)(ByVal textControl As ITextControl, ByVal defaultValue As resultType) As resultType
    Return ConvertValue(Of resultType)(textControl .Text, defaultValue)
End Function

Now, I need the value of a HiddenField (which does not implement the ITextControl interface). It would be easy enough to overload the function, but it would be awesome to just handle the base class and not have to deal with writing overloads anymore.

Edit - additional info

Here is an example of how the extension method is used

Dim myThing as Decimal = tbSomeWebControl.GetTextValue(Of Decimal)(0)  ' Converts the textbox data to a decimal
Dim yourThang as Date = hdnSomeSecret.GetTextValue(Of Date)(Date.MinValue)  ' Converts the hiddenfield data to a Date

Currently, this requires two overloads because data is accessed using the Value property in hiddenfields and the Text property in textboxes.

What's wrong with overloads? Nothing, except i'm writing nearly the same code over and over (just modifying the 1st parameter of the definition and the 1st argument of its call).


Solution

  • You cant use object-oriented approach to get the value, because they don't have common ancestor in inheritance tree from which you can fetch data. Solutions are so inelegant that you should just pass WebControl and check type dynamically.

    Pardon my C#:

    (note that this is notepad code, I didn't run any of it so you may need some tweaks)


    Solution 1: Get data directly from Request

    Downside: Not very ASP.NET-ish. Does the job however.

    public string GetTextValue<ResultType>(WebControl control, ResultType defaultValue)
    {
        // If you only wish data from form, and not questy string or cookies
        // use Request.Form[control.UniqueId] instead
        string value = Request[control.UniqueId];
        return ConvertValue<ResultType>(value, defaultValue);
    }
    

    Solution 2: Check type dynamically

    Downside: you have to provide handling for multiple control types

    public string GetTextValue<ResultType>(WebControl control, ResultType defaultValue)
    {
        if (control is ITextControl)
            return ConvertValue<ResultType>((ITextControl)control.Text, defaultValue);
        else if (control is HiddenField)
            return ConvertValue<ResultType>((HiddenField)control.Value, defaultValue);
        else if (anothertype)
            return ConvertValue<ResultType>(another_retrieval_method, defaultValue);
    }
    

    Solution 3: Use reflection

    Downside: reflection can be tricky, doesn't look elegant, and can be slow on many invocations

    public string GetTextValue<ResultType>(WebControl control, ResultType defaultValue)
    {
        // Get actual control type
        Type controlType = control.GetType();
        // Get the attribute which gives away value property
        Attribute attr = controlType.GetCustomAttribute<ControlValuePropertyAttribute>();
        if (attr == null)
            throw new InvalidOperationException("Control must be decorated with ControlValuePropertyAttribute");
        ControlValuePropertyAttribute valueAttr = (ControlValuePropertyAttribute)attr;
        // Get property name holding the value
        string propertyName = valueAttr.Name;
        // Get PropertyInfo describing the property
        PropertyInfo propertyInfo = controlType.GetProperty(propertyName);
        // Get actual value from the control object
        object val = propertyInfo.GetValue(control);
        if (val == null)
            val = "";
    
        return ConvertValue<ResultType>(val, defaultValue);
    }