DataSource parameter for code-behind property

Here's some code for a server control "PropertyParameter" that you can use as a SelectParameter in your xyzDataSource that binds (one-way!) to a property value that is defined in code behind. This is something I need sometimes and isn't available out-of-the-box.

This method uses reflection and requires the property in code-behind to be public. There are two implementations, one for a Page and one for a UserControl. You will need to specify the Target property if you use it inside a UserControl.

This is how you use it in your markup:

<asp:ObjectDataSource ID="StatusObjectDataSource" runat="server"
    TypeName="Components.WorkflowSecurity.WorkflowComponent"
    SelectMethod="GetAvailableStatusesForEntityType">
    <SelectParameters>
        <controls:PropertyParameter Name="entityType" 
            PropertyName="EntityType" Target="Page" />
    </SelectParameters>
</asp:ObjectDataSource>

Here is the the code for the server control:

public class PropertyParameter : Parameter
{
    public enum PropertyTargetEnum
    {
        Page,
        UserControl
    }
 
    public string PropertyName { get; set; }
    public PropertyTargetEnum Target { get; set; }
 
    public PropertyParameter()
    {
        Target = PropertyTargetEnum.Page;
    }
 
    protected override object Evaluate(
        HttpContext context, Control control)
    {
        if (string.IsNullOrEmpty(PropertyName))
        {
            throw new ArgumentNullException("PropertyName");
        }
 
        Control targetControl = ResolvePropertyTarget(control);
 
        Type pageType = targetControl.GetType();
 
        PropertyInfo pi = pageType.GetProperty(PropertyName, 
            BindingFlags.Instance | 
            BindingFlags.Public | 
            BindingFlags.NonPublic);
 
        if (pi == null)
        {
            throw new InvalidOperationException(
                string.Format("Property '{0}' not found on {1}.", 
                PropertyName, Target));
        }
 
        return pi.GetValue(targetControl, null);
    }
 
    private Control ResolvePropertyTarget(Control control)
    {
        switch (Target)
        {
            case PropertyTargetEnum.Page:
                return control.Page;
 
            case PropertyTargetEnum.UserControl:
                return FindParentUserControl(control);
        }
 
        throw new ArgumentException("Invalid target type.");
    }
 
    private UserControl FindParentUserControl(Control control)
    {
        Control parent = control.Parent;
 
        while (parent != null)
        {
            if (parent is UserControl)
            {
                return (UserControl)parent;
            } 
 
            parent = parent.Parent;
        }
 
        throw new ArgumentException(string.Format(
            "'{0}' is not inside a UserControl.", 
            control.ID));
    }
}

Btw, there are two common workarounds if you don't like this implementation:

  • Store the value in a HiddenField and use a ControlParameter instead
  • Add a Parameter control; implement the OnSelecting event of the DataSource to set the value on the InputParameters collection

2 Comments

  • That was just what i needed

  • Thanks. This works a treat at run-time, however it errors in the designer with:

    ObjectDataSource1: 'SelectParameters' could not be initialized. Could not be added to the collection. Details: 'UserID' could not be set on property 'PropertyName'


    Not a problem as such, just annoying.

Comments have been disabled for this content.