In Depth Banner
Skip Navigation LinksWelcome > In Depth articles > Property extender

Select your preferred language

The design time environment of Visual Studio is a far cry from even relatively recent versions of the IDE prior to the advent of .NET. C++ users had a very much more basic experience than that of Visual Basic users but now, the drag and drop Rapid Application Development environment is here to stay.

One of the greatest features of Visual Studio.NET is the ease with which a programmer can create a control and use it in the design time, also in a very RAD fashion.

There are many controls and components in the .NET framework namespaces that can be used to create great applications very quickly but it's true to say that some of them could be improved a wee bit. Interestingly enough. Visual Studio provides us with the opportunity to make those improvements without even changing the control we want to modify.

This can be accomplished by creating an Extender Provider that uses features of the designer to seemingly add properties and abilities to component or control.

An extender provider does the following.

  • Adds one or more properties to a specific type of component or a range of components.
  • Uses the events, methods or properties of the extended component to perform some action at run-time

Interfaces and attributes

An extender provider relies on one interface and an attribute for it's abilities. The interface is IExtenderProvider and the attribute is ProvidePropertyAttribute.

At design time, whenever a component or control is selected and placed in the property editor, the IDE will query all the components on a form to see if they implement IExtenderProvider. If they do the control is given the opportunity to say whether it provides extra properties for the control. This is done through the CanExtend method. If the component has properties to add to a specific control the CanExtend method should return true.

When a component can extend another, the actual property or properties to be added, there can be more than one, are declared using the ProvidePropertyAttribute. For each property provided there must be some accessor methods and these are written into the ExtenderProvider as ordinary methods with a special name. For example, if you provide the property "Fred" you will make methods called "GetFred" and "SetFred" in the extender provider.

The Getxxx and Setxxx methods are passed the reference to the component being extended. It's up to your code to store or retrieve the property data associated with that particular control reference. The normal way of doing this is to associate the reference with the data in a hash table. Any other method of storage you like is also allowed.

The bare bones

Here is the bare bones of a simple extender provider. It enables you to add a string of text to any MenuItem object that can be used to display menu help on a status bar or status bar panel.

using System;

using System.Drawing;

using System.Collections;

using System.ComponentModel;

using System.Windows.Forms;

 

namespace WellFormed

{

  /// <summary>

  /// MenuStatusExtender adds a text message to MenuItem objects that can be displayed in the application's status bar.

  /// </summary>

  [

  ToolboxItem(true),

  ToolboxBitmap(typeof(MenuStatusExtender),"MenuStatusExtender.bmp"),

  ProvideProperty("StatusText",typeof(MenuItem))

  ]

  public class MenuStatusExtender : Component, IExtenderProvider

  {

 

    private Hashtable _extendees=new Hashtable();

 

    public MenuStatusExtender()

    {

    }

 

    #region IExtenderProvider Members

 

    public bool CanExtend(object extendee)

    {

      if(extendee is MenuItem)

        return true;

      return false;

    }

 

    #endregion

 

    public string GetStatusText(MenuItem item)

    {

      if(_extendees.ContainsKey(item))

        return (string)_extendees[item];

      else

        return "";

    }

 

    public void SetStatusText(MenuItem item, string statustext)

    {

      if(_extendees.ContainsKey(item))

        _extendees[item]=statustext;

      else

        _extendees.Add(item,statustext);

    }

  }

 

}

Doing something useful

When an ExtenderProvider has insinuated it's properties into another object, something useful has to be done with them. At runtime however, nothing in the framework cares about the extender provider like the IDE does so it's left to fend for itself as far as finding out exactly how to deal with this new information it is holding on behalf of it's extendees.

This is when events can be used to bring the extender provider to life at the right moment.

The code in the demo shows a simple extender provider that can store a string and associate it with a menu item. The goal is to have that string turn up on the status bar of the form whenever the user floats their mouse over the menu. As the menu operations take place, the menu's generate a stream of events that can be handled and something useful done with them. In the case of this particular job, our interest lies in the MenuItem.Select event.

The extender provider will make an event available which can provide a string containing our message. To create this event, a delegate is created and a new event argument supplied that can be used to pass the message on to our code.

The delegate and the event argument definition is listed below.

  public delegate void ItemSelectEventHandler(object sender, ItemSelectEventArgs e);

 

  public class ItemSelectEventArgs : EventArgs

  {

 

    protected ItemSelectEventArgs()

    {

    }

 

    public ItemSelectEventArgs(string message)

    {

      _statusMessage=message;

    }

 

    string _statusMessage;

 

    public string StatusMessage

    {

      get{return _statusMessage;}

    }

  }

Once the delegate and event arguments are in place it's possible to have the extender provider wire itself to the events of the extendee. The modification to the SetStatusText method and the other required code to provide an event in the extender is shown below.

    public void SetStatusText(MenuItem item, string statustext)

    {

      if(_extendees.ContainsKey(item))

        _extendees[item]=statustext;

      else

      {

        _extendees.Add(item,statustext);

        item.Select+=new EventHandler(MenuStatusExtender_Select);

      }

    }

 

    private void MenuStatusExtender_Select(object sender, EventArgs e)

    {

      OnItemSelected(new ItemSelectEventArgs((string)_extendees[sender])); 

    }

 

 

    protected void OnItemSelected(ItemSelectEventArgs e)

    {

      if(ItemSelected!=null)

        ItemSelected(this,e);

    }

 

    public event ItemSelectEventHandler ItemSelected;

Good housekeeping.

The MenuStatusExtender is almost ready. All that needs to be done now is ensure that when the control is destroyed, all references to Select events in the various menu items are removed. To do this, a destructor and dispose override are added to the component as shown below.

    ~MenuStatusExtender()

    {

      Dispose(true);

    }

 

    protected override void Dispose(bool disposing)

    {

      foreach(object o in _extendees.Keys)

        ((MenuItem)_extendees[o]).Select-=new EventHandler(this.MenuStatusExtender_Select);

 

      _extendees.Clear();

 

      base.Dispose (disposing);

    }

Testing the MenuStatusExtender.

Once compiled, the component may be placed in the toolbox by dragging it or by right clicking the toolbox, selecting "Add/remove items" and browsing to the MenuStatusExtender DLL.

By virtue of the attributes on the class and the bitmap placed in the resources, the icon for the tool will be seen in the toolbox Figure 1 shows this process.

Figure 1. Placing the control in the toolbox.

The MenuStatusExtender can now be dragged to a form. When a MainMenu and some MenuItems have been added, you'll see the new property "StatusText on menuStatusExtender1" in the property grid. Status strings may now be added to the MenuItems.

Selecting the menuStatusExtender1 component in the tool tray below the form will enable you to add an event handler to the extender which will inform your code when a status message us available. This message can then be placed in a status bar or indeed, any other item that displays text. Figure 2 shows this in action on a simple form.

Figure 2: Status is displayed

Figure 3 shows the form designer at work.

Figure 3: Editing the extended properties.

Finally, the event handler for the MenuStatusExtender.ItemSelected event is shown below.

    private void menuStatusExtender1_ItemSelected_1(object sender, WellFormed.ItemSelectEventArgs e)

    {

      this.statusBar1.Text=e.StatusMessage;

    }

Summary

This article has shown you how to create an extender provider component, how to use the Getproperty and Setproperty methods to store and retrieve data which is associated with another item on the form and how to use the events of the extended item to retrieve the data at runtime.

All sources for the MenuStatusExtender project can be downloaded from below locations.


Return to the main index.

Copyright © Bob Powell 2003-2009. All rights reserved