Code Less and Do More with Extender Controls

n our previous article “Put a 24-hour Lockdown on Your .NET UIs,” we discussed various ways to augment user interfaces to interpose underlying business logic and data engine security constraints. We recommended the use of extender controls. The purpose behind this suggestion was clear: We don’t like to code. In fact, if given a choice between coding and surfing, only one question remains?where’s the wax? While it is certainly possible and in some cases necessary to subclass existing controls, we prefer to reserve such methods for situations just shy of thermonuclear destruction.

However, one thing we did not expect when we made our suggestion was that some of our readers would be unfamiliar with the details of extender controls, their creation, and their operation. While such a discussion was outside the scope and coverage of the original article, we decided that it would make a fine subject for whole new article. So without further gilding the lily and with no more ado, we present to you our champion, the Knight of the Lazy Programmer, the Extender Control.

Extender Controls: What Are They
An extender control is a non-visual control (one added to the tray area of a form rather than the form itself) that “extends” the functionality of the Visual Studio designer. MSDN defines an extender provider control as: a component that provides properties to other components. For example, when a ToolTip Component (Windows Forms) component is added to a form, it provides a property called ToolTip to each control on that form. The ToolTip property then appears in the Properties window for each control and allows the developer to set a value for this property at design time.” In short, extender controls are a short cut. They allow the VS designer to extend the displayed properties of a control so that from your standpoint the control is different. It’s sort of like putting a new air freshener in your old Chevy?the car doesn’t run any better but it sure smells nice.

The important distinction however, is that extender controls only simulate the extension of properties to the control during design time. At runtime, any attempt to access the property as a member of the “extended” control will not run (in fact it will not even compile). The control is no different, just like that old Chevy. To access the methods and properties of the “extended” control at runtime, you must enumerate the extended properties and methods of the non-visual extender control itself.

Developing an Extender Control
An extender control, or, extender provider, if we want to get literal, is a component derived from System.ComponentModel.Control that implements the IExtenderProvider interface.

public class SecurityEnableComponent : System.ComponentModel.Component,                                       System.ComponentModel.IExtenderProvider 

The interface itself is extraordinarily simple and merely requires the implementation of one method: “CanExtend.” The development environment calls the Can Extend to determine whether a given control can be extended by the extender control. In most instances, the implementation of this method is accomplished through a very simple type check and only when it’s necessary to prevent the extender from accidentally being applied to controls for which it is inappropriate or incapable of servicing. The code below shows an example of the “CanExtend” method from the SecurityEnableComponent control discussed in the previous article. Notice that the implementation of this method tests the type of an object provided by the caller and returns true if the target is either a Control or a Menu Item. This ensures that the Extender is only visible in the design-time properties for those categories of components and ignores all other types of components. The additional check (target is not SecurityEnableComponent) in this instance is superfluous because the class is derived from Component and not Control. Nonetheless, it is good form and occasionally necessary for some implementations. It’s purpose is to ensure that the extender is not used to extend itself.

bool IExtenderProvider.CanExtend(object target) {  if((target is Menu || target is Control) && !(target is SecurityEnableComponent))    return true;  else    return false;}

Okay, What’s the Catch?
Implementing CanExtend only allows the development environment to assess whether a class can be utilized to extend a given control. It does not provide the development environment with any information about how the extended property is to function. In order to provide that functionality, custom methods must be added to the extender control. These methods are specifically named and are called by the development environment wherever it needs to change or retrieve the properties that are being added to the controls on the form. From this you can correctly conclude that you can use a single extender control to add multiple custom properties to the controls of a form. Two custom methods are required to implement a single extended property: one to set values and one to retrieve them. Again, these methods must be specifically named:

Get + [property name] Set + [property name]

This naming convention is required so that the development environment can locate the methods used to implement the property within the extender provider. It seems somewhat ironic that you are required to use methods to implement a property. It’s because the set method requires two parameters in order to operate (and the get method requires one). In both cases, this is one parameter more than is permitted in a typical property implementation.

The Set method accepts two parameters: the component to be extended and the value to which that property is set. The first parameter keeps track of what extended properties have been set to what values for which components of the form. Therefore, any implementation of any extender control needs to include some form of collection management. This example utilizes a hashtable. The set method inserts the value within the collection for the property with the value supplied by the method call (if the value is already there, the set method simply replaces it). Thus, the component parameter acts as the key in the collection object’s key value pair.

public void SetSecurityEnableComponent(System.ComponentModel.Component component, string value) {  if (value == null)     value = string.Empty;  if (value.Length == 0)    roleTexts.Remove(component);  else     roleTexts[component] = value;}[DefaultValue("")]public string GetSecurityEnableComponent(System.ComponentModel.Component  c) {  string text = (string)roleTexts[c];  if (text == null)     text = string.Empty;  return text;}

The get method is far simpler. When this method is called (queried) for a component, it either returns (replies) with the value stored in the collection for the given component or it returns a default value if value has been applied for the extended property for the given control.

To review:

  • Declare the extender provider component to be derived from System.ComponentModel.Component and to implement the IExtenderProvider interface.
  • Provide a CanExtend method to specify what form controls the provider will extend.
  • Provide a Get and Set method for the extended properties you wished to provide.
  • Provide an internal mechanism to keep track of the values of your extended properties for multiple form controls.

Are We There Yet?
There is only one more crucial element required to pull all this together. You need to tell the designer that the property is available and tell it how to set/retrieve the property (ie. is the property a string, a list of predefined values, etc.). You can do this by providing a class level custom attribute, specifically, the ProvideProperty attribute, which provides critical keys to the design time environment.

[ProvideProperty("SecurityEnableComponent",typeof(System.ComponentModel.Component ))][ProvideProperty("Two",typeof(System.ComponentModel.Component ))]public class SecurityEnableComponent : System.ComponentModel.Component,                                       System.ComponentModel.IExtenderProvider 

The ProvideProperty attribute relays the name of the property. In the example above, the property becomes SecurityEnableComponent.

Next, it specifies the controls or components to which each property can be applied. In this case, the property should be applied to all components. Remember, you can use the CanExtend method for more fine-grain control over specific classes of components.

Finally, you can use ProvideProperty to tell the designer how to display and manipulate the value of the property. In this example, and in most cases, the designer can use a default property editor (such as the string property editor). Obviously, more complex properties might require a custom property editor (such as a font property or an RGB color property). In these cases, you would specify a custom property editor as the third attribute of the ProvideProperty constructor.

The Value Add
The extended properties will not be available in the controls themselves at runtime. In order to access the property values, you need to access the extender provider control directly. Now all you need to do is implement it. Simply package the component into an assembly class library (DLL), use the System.Drawing.ToolboxBitmapAttribute to provide a custom Toolbox Icon, and add the project into the IDE’s Toolbox. Now, you’re hooked up?code less, do more; have fun!

Share the Post:
Share on facebook
Share on twitter
Share on linkedin

Overview

Recent Articles: