Sign in  
Disclaimer =  About us 
 
      

Extender provider components in ASP.NET

Introduction

Visual Studio offers an elegant method to add extra properties to all or some types of controls, in a way that resembles multiple inheritance. Traditionally you would have to subclass all controls that need an extra property, put them in a class library and add the subclassed controls to the Toolbox for design-time support. The alternative is to create a component that implements the IExtenderProvider interface. Drop it in the component tray and Visual Studio shows its properties with all suitable controls on the form. The WinForms ToolTip provider is an example of a provider component.

This article assumes you have a basic understanding of the way provider components work. If you haven't encountered the IExtenderProvider before, read one of the introductory articles first:

The IExtenderProvider mechanism is usually described for use in WinForms, and there's a good reason for that. At the bottom of the MSDN library article it says:

NoteThe implementation of an extender provider for Windows Forms controls is different from that for
ASP.NET server controls.

That's right. It doesn't work for ASP.NET server controls, because the required design-time support in Visual Studio 2003 is broken. The good news is that there's a workaround.

This article presents a class that can be used to develop extender providers for ASP.NET server controls. First we show what's going wrong in Visual Studio and how we fix it. If you're only interested in the component, skip that part and read only the section Using the component.

We only discuss extender providers implemented as components. The DefaultButtons control by Andy Smith is an example of an extender provider based on a web control.

What's wrong with Visual Studio?

Let's say that we want to write an extender provider, MyProvider, that does something terribly useful. It adds a single property to all web controls, a boolean called DoMagic:

[ProvideProperty("DoMagic", typeof(System.Web.UI.Control))]
public class MyProvider : System.ComponentModel.Component, IExtenderProvider
{
  // IExtenderProvider implementation

  public bool CanExtend (object AExtendee)
  {
     return (AExtendee is System.Web.UI.Control)
  }

  // Retrieve the value of the DoMagic property for a control
  public bool GetDoMagic (object AExtendee)
  {
    if (_Values.ContainsKey (AExtendee))
    {
      return _Values[AExtendee];
    }
    else
    {
      return false;
    }
  }

  // Set the value of the DoMagic property for a control
  public void SetDoMagic (object AExtendee, bool AYes)
  {
    _Values[AExtendee] = AYes;
    if (AYes)
    {
      (AControl as System.Web.UI.Control).PreRender += new EventHandler (MagicStuff);
    }
  }

  private void MagicStuff (object sender, EventArgs e)
  {
    if (GetDoMagic (sender))
    {
      // Here the magic happens
    }
  }

  // The provider component stores the property values
  private Hashtable _Values = new Hashtable ();
}

Put this component in a class library, compile the library, add the component to the Toolbox and drop it in the component tray of a new ASP.NET page. All web controls in the page now have an extra property, DoMagic. Visual Studio automatically adds an InitializeComponent method to the code behind the page:

  private void InitializeComponent()
  {
    this.MyProvider1 = new MyProvider();
  }

Add a label and a textbox to the page and set the value of DoMagic to true for both of them. You would expect that the InitializeComponent method now reads:

  private void InitializeComponent()
  {
    this.MyProvider1 = new MyProvider();
    this.MyProvider1.SetDoMagic (this.Label1, true);
    this.MyProvider1.SetDoMagic (this.TextBox1, true);
  }

But instead it says:

  private void InitializeComponent()
  {
    this.MyProvider1 = new MyProvider();
    this.MyProvider1.SetDoMagic (this, false);
  }

Visual Studio does not know how to store the values for provided properties! It should add a SetDoMagic call for each control that has a non-default property value, but instead it adds a single call to SetDoMagic for the page object, always passing the default property value. A little more research shows that it adds a SetXXX call for each property XXX that has been declared by a ProvideProperty attribute for a type that matches the page object, provided that the extender can extend the page object.

Contents

Downloads

Also available on  CodeProject
26 july 2004