Click here to Skip to main content
Click here to Skip to main content
Go to top

Using Property Extender Providers with ASP.NET 2.0

, 11 Jul 2005
Rate this:
Please Sign up or sign in to vote.
Implementation of a Property Extender WebControl in ASP.NET 2.0 that solves the issue of design-time serialization.

Introduction

Property extender providers are widely used in the Windows Forms environment where this technology enjoys full support of the .NET Framework and Visual Studio IDE. Conversely ASP.NET developers do not always fully appreciate the benefits of the technique. This article demonstrates how to take advantage of the property extenders in ASP.NET custom control projects and suggests practical solutions to issues with design-time serialization and state management posed by ASP.NET 2.0 and Visual Studio 2005.

There is a common case of user interface requirements that calls for controls presented on a Web Form to expose a specific set of custom properties. For instance certain controls could disable themselves based on current user credentials or display some sort of interactive context-sensitive help. Most often web developers tend to realize such use cases by creating libraries of custom controls representing subclasses of original controls however employing a property extender provider could prove to be the more elegant and effective technique under a wide variety of circumstances.

Extender providers add new properties to other controls. Components or controls acting as property providers are responsible for the behavior of the provided properties.

The world of Windows Forms Visual Studio renders full support to property extenders including built-in CodeDom serialization thus making the implementation simple and straightforward. On the other hand, developers of custom web controls and components exposing extender providers were forced to custom-code their own serialization logic. In most cases this approach worked better with non-visual designable components as opposed to more common visual controls until Visual Studio 2005 eliminated components and CodeDom serialization from the developer’s arsenal altogether.

Background

CodeProject has published a number of articles on this subject:

In this article, I would like to introduce in broad terms the concept of property extenders to the ASP.NET practitioners and share some ideas on how to address issues with serialization of provided properties specifically when working with ASP.NET 2.0 and Visual Studio 2005.

Anatomy of property extender

An extender provider component can provide properties to other components in a container. In a sense these other components are acting as clients of an extender provider. One well-known example is an ErrorProvider component. When an ErrorProvider control is added to a form all other controls have an Error property added to their list of properties.

IExtenderProvider interface

IExtenderProvider interface allows the design environment to query method CanExtend to identify all the objects in a container capable of receiving the extender properties.

public bool CanExtend(object extendee)
{
    return extendee is TextBox;
}

ProvidePropertyAttribute

This class-level attribute defines the name of the property offered by a property extender to other components. The class marked by the attribute has to provide implementation of the property code in the form of Set<name> (if required) and Get<name> methods.

[ProvideProperty("Validation", typeof(TextBox))] 
public class ValidatorControl
…
public TextValidator GetValidation(TextBox textBox)
{ …

As you can see property extender concept is fairly straightforward and permits a great degree of flexibility when adding new features to components in your project. We will cover specifics of building property extenders for ASP.NET applications next.

Property extenders in ASP.NET

To better illustrate this fairly complex topic all concepts and techniques are going to be discussed in the context of the sample project MutlifieldValidator (source is attached with this article). It has been developed in C# 2.0 with Visual Studio 2005 Beta 2. While coding I restrained from using generics and other new framework features not available in .NET 1.1 and Visual Studio 2003, therefore the project can be easily ported to that environment as well.

Multi-field validator overview

MutlifieldValidator solves the proverbial problem of validating text input with the somewhat ambitious aim of establishing a strong relevance to the practical development tasks in this purely theoretical exercise. This choice of problem domain also allows readers to easily compare this solution with the existing ‘out-of-the-box’ ASP.NET validator control in such areas as usability and functional simplicity.

The following requirements definitions are assumed to drive our preference in technology to property extenders:

  • Each text box on a form should have an individually specified regular expression for validation and an error message that will be displayed when the regular expression is not matched by the user input.
  • Standard validator controls are not a preferable option due to limited real estate in this application where the error message for all validated controls share a single label for display.

As directly inferred by our requirement the property extender control will have to provide two properties for each text box – ErrorMessage and Expression. In the best traditions of good design practices instead of providing these properties individually we will encapsulate them into a class called TextValidator. Since we also need a reference to the hosting property extender and an ID of a client control these items are reflected in the properties HostingControl and Target respectfully.

Placing a proper type converter attribute on the class level [TypeConverter(typeof(ExpandableObjectConverter))] will allow client text boxes to show these properties in an elegantly expandable manner in the designer’s property grid.

We are going to hide HostingControl and Target from a property grid by marking them with [Browsable(false)] attribute.

ValidatorControl class is inherited from System.Web.UI.WebControl.Label and therefore exposes all the attributes of a Label, making itself useful as a means of display for error messages as well as housing our property extender control.

As you can see from the diagram above the ValidatorControl implements IExtenderProvider interface by defining CanExtend method i.e. limiting the scope of ‘client’ controls to text boxes.

public bool CanExtend(object extendee)
{
    return extendee is TextBox;
}

To actually define a provided property we are using a ProvideProperty attribute and a Get method for implementing the property’s logic. Set method is not required since the provided property is a TextValidator that handles its internal state.

[ProvideProperty("Validation", typeof(TextBox))]
…
public TextValidator GetValidation(TextBox textBox)
{   
    return FindValidation(textBox);
}
…

FindValidation method will have to find the corresponding TextValidator in the encapsulated collection for each instance of TextBox passed or create a new TextValidator if none is found.

ValidatorControl also implements IValidator interface making it a part of validators collection owned by a Web Form hosting it. Both this feature and the actual validation logic based on regular expressions are rather trivial and the source code in the sample project is self-explanatory. Those details are omitted from this narrative.

Why it does not work

Having performed all these or similar steps you should have a fully functional property extender control, and you probably would if this was done in a Windows Forms environment. So what is so different with Web Forms? If you test your control on an actual Web Form you will discover that the property values assigned to ‘client’ controls are ‘disappearing’ within the same design session and there is no persisting into the run-time. Clearly the issue is property serialization. In Windows Forms case, the designer is does all the dirty work serializing extended properties into CodeDom within the ComponentInitialize method of a form class but a Web Form designer of Visual Studio 2003 seems to be unaware of the extended properties. It does however have and maintain the same ComponentInitialize canvas method making it possible to write custom serialization for your properties with some fancy CodeDom footwork (see article by Wouter van Vugt for details). However there is one tough catch. ComponentInitialize and CodeDom serialization are not available in Visual Studio 2005 as of Beta 2 release. These features have been ‘discontinued’ in favor of more straightforward HTML-based persistence. This means that if we want to see these properties serialized we will have to find an alternative to CodeDom.

Solving the issue of serialization for provided properties

There are a number of helpful attributes that allow WebControls to serialize and deserialize their collection-based properties. Since our ValidatorControl is exposing the collection of TextValidator objects as property named Validators (where each member of the collection is responsible for validating a TextBox) we can take advantage of those attributes. Given below is the declaration of the property:

[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
[PersistenceMode(PersistenceMode.InnerProperty)]
[Browsable(false)]
public TextValidatorCollection Validators
{
   get
   {
       return m_Validators;
   }
}
  • DesignerSerializationVisibility instructs Web Form design environment to persist this property as content i.e. HTML tags versus ComponentInitialize.
  • PersistenceMode attribute tells the designer to save the property as InnerProperty, that is to serialize our collection between the opening and closing tags of ValidatorControl.
  • We do not wish to expose this property in designer’s property grid therefore we mark it with Browsable(false) attribute.

This is an example of the resulting HTML:

<cc1:ValidatorControlID="multiFieldValidator"
    runat="server"IsValid="True"Font-Bold="True"
    Font-Size="Small"ForeColor="Red">
<Validators>
<cc1:TextValidatorExpression="^\d{0,10}$"
    Target="txtPhone"
    ErrorMessage="phone number should include only numerics">
</cc1:TextValidator>
<cc1:TextValidatorExpression="^[a-zA-Z]{0,16}$"
    Target="txtLastName"
    ErrorMessage="last name should include only alpha characters">
</cc1:TextValidator>
<cc1:TextValidatorExpression="^[a-zA-Z]{0,16}$"
    Target="txtFirstName"
    ErrorMessage="first name should include only alpha characters">
</cc1:TextValidator>
</Validators>
</cc1:ValidatorControl>

As you can see all the TextValidator objects are neatly tacked in within the ValidatorControl tags. This takes care of serialization. Now what should we do about deserializing the TextValidators?

There is another attribute that can make this task a trivial one. It is called ParseChildren and it is placed on a class level. While applying the attribute we specify that one of the properties in this control will have to be deserialized as a child collection and we also have to indicate specifically which property it is. This is how it is done:

[ParseChildren(true, "Validators")]
public class ValidatorControl : Label, IExtenderProvider, 
                                                 IValidator
{ …

Now that we have the serialization and deserialization covered there is still one small problem. If you change the values of extended properties in design-time you will notice that the Web Form designer is not always updating the HTML tags representing your ValidationControl. This happens because Visual Studio does not recognize the changes made from within a TextBox property grid as in any way related to the ValidatorControl. Let’s see what we can do about it.

Correcting design-time behavior

Since there is obvious gap in Web Form designer functionality that prevents ValidationControl designer from being notified of changes in its state we will have to hand-code this wiring ourselves. Here is a method in ValidatorControl that will ‘refresh’ design-time representation of our Validators property whenever we need it to:

internal void NotifyDesigner()
{
    if (this.DesignMode)
    {
        IDesignerHost dh = 
            this.Site.Container as IDesignerHost;
        LabelDesigner designer = 
            dh.GetDesigner(this) as LabelDesigner;
        PropertyDescriptor pd =
        TypeDescriptor.GetProperties(this)["Validators"];
        ComponentChangedEventArgs ccea = 
          new ComponentChangedEventArgs(this, pd,
          Validators, Validators);
          designer.OnComponentChanged(this, ccea);

    }
}

What happens here is we are telling the designer to refresh its rendering of the ValidatorControl by explicitly sending ComponentChanged event to the design-time environment. This will cause Visual Studio to re-render HTML tags of the control. As we employ a custom-built collection (TextValidatorCollection) to store our TextValidators we can ensure that each member in the collection has a reference to its parent (internal property and a member variable private ValidatorControl m_Control;).

In the exposed properties of TextValidator, I simply call ValidatorControl's NotifyDesigner method every time the property is set.

[Description("regex expression for validating a textbox")]
public string Expression
{
    get
    {
        return m_Expression;
    }
    set
    {
        m_Expression = value;
        NotifyDesigner();
    }
}
/// notifies hosting control that a property
/// has been changed (check for null for run-time)
private void NotifyDesigner()
{
    if (m_Control != null)
    {
        m_Control.NotifyDesigner();
    }
}

This simple technique will make the ValidatorControl correctly reflect the changes that are made to the extended properties at design-time. Finally, we have a fully functional property extender control that plays nice with ASP.NET 1.1 and ASP.NET 2.0.

Conclusion

As this article demonstrated, there is a fairly straightforward way to leverage powerful extender technology in ASP.NET projects. Coding effort involved in setting up a property extender control is very well balanced by the flexibility and cleanness of the final solution. Developer’s attempts to introduce this .NET feature into Web Forms were somewhat discouraged by the changes that ASP.NET 2.0 has brought about but as we have proven there is a way to overcome the difficulties with serialization of extended properties and Visual Studio designer event model.

This paper is by no means a final word on the future of property extenders in ASP.NET environment and the sample project does have its limitations but it provides enough of a starting ground and brings in a new perspective to building elements of complex user interface. I am sure there are significant opportunities for design and experimentation that may lead to a better or a simpler model. Happy coding!

History

  • 7/11/2005 - Original article posted.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

Share

About the Author

Alex Soldatov

United States United States
No Biography provided

Comments and Discussions

 
Questionextending this extender PinmemberJean Blignaut10-Oct-07 2:22 
AnswerRe: extending this extender PinmemberAlex Soldatov14-Oct-07 11:41 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web01 | 2.8.140926.1 | Last Updated 11 Jul 2005
Article Copyright 2005 by Alex Soldatov
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid