Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C#

Remove Unwanted Properties and Events from UserControl

4.87/5 (16 votes)
14 Oct 2014CPOL2 min read 25.4K  
Use an ICustomTypeDescriptor implementation to remove all access to inapplicable properties and events from a UserControl.

Introduction

Have you ever created a UserControl and wanted to remove all those properties that are not applicable? And what about the events? Going through and overriding the properties with the BrowsableAttribute set to false is awkward, especially since some of these properties are virtual, while others aren't (need to be declared as 'new'). This technique removes the unwanted properties and events by simply adding their names to a list.

Background

I am currently writing a control that performs image transitions, and am planning a separate article for that. After overloading a number of properties froim the base class, and adding the [Browsable(false)] attribute to them, I started thinking there must be a way to do this within the code itself. I used this technique to remove unwanted properties and events from that control, and I felt that the technique used deserved its own article.

Using the Code

The VS designer and editor uses the TypeDescriptor of your object to get a list of its members (methods, properties and events) that are available. By implementing the ICustomTypeDescriptor on your class, you get to choose which of these are actually available to the host of the object. This makes it possible to filter any of the menber you don't want used by the consumer of the control.

C#
public partial class ucImageShow : UserControl , ICustomTypeDescriptor {
     
     ...
     }
}

Most of the implentation uses the static methods of the TypeDescriptor object to return all the information from the framework. For the required methods, we just obtain that information, and filter out whatever we don't want. Below is my implementation for the image transitioning control.

C#
public AttributeCollection GetAttributes() {
    return TypeDescriptor.GetAttributes(this, true);
}

public string GetClassName() {
    return TypeDescriptor.GetClassName(this, true);
}

public string GetComponentName() {
    return TypeDescriptor.GetComponentName(this, true);
}

public TypeConverter GetConverter() {
    return TypeDescriptor.GetConverter(this, true);
}

public EventDescriptor GetDefaultEvent() {
    return TypeDescriptor.GetDefaultEvent(this, true);
}

public PropertyDescriptor GetDefaultProperty() {
    return TypeDescriptor.GetDefaultProperty(this, true);
}

public object GetEditor(Type editorBaseType) {
    return TypeDescriptor.GetEditor(this, editorBaseType, true);
}

// The following 2 methods will get the EventDescriptor objects for all events declared in
// this user control, included those inherited from the UserControl object and its ancestors.
// We then call the FilterEvents method to return a new EventDescriptorCollection with the 
// required events removed.
public EventDescriptorCollection GetEvents(Attribute[] attributes) {
    EventDescriptorCollection orig = TypeDescriptor.GetEvents(this, attributes, true);
    return FilterEvents(orig);
}

public EventDescriptorCollection GetEvents() {
    EventDescriptorCollection orig = TypeDescriptor.GetEvents(this, true);
    return FilterEvents(orig);
}

// The following 2 methods will get the PropertyDescriptor objects for all properties declared in
// this user control, included those inherited from the UserControl object and its ancestors.
// We then call the FilterProperties method to return a new PropertyDescriptorCollection with the 
// required properties removed.
public PropertyDescriptorCollection GetProperties(Attribute[] attributes) {
    PropertyDescriptorCollection orig = TypeDescriptor.GetProperties(this, attributes, true);
    return FilterProperties(orig);
}

public PropertyDescriptorCollection GetProperties() {
    PropertyDescriptorCollection orig = TypeDescriptor.GetProperties(this, true);
    return FilterProperties(orig);
}

public object GetPropertyOwner(PropertyDescriptor pd) {
    return this;
}

Filtering the events and properties is simply a creating a new collection, and adding all members of the existing collection that do not meet the filter criteria. This new collection then replaces the original collection for the return of the ICustomTypeDescriptor method.

C#
private string[] _excludeBrowsableProperties = {
    "AutoScroll",
    "AutoScrollOffset",
    "AutoScrollMargin",
    "AutoScrollMinSize",
    "AutoSize",
    "AutoSizeMode",
    "AutoValidate",
    "CausesValidation",
    "ImeMode",
    "RightToLeft",
    "TabIndex",
    "TabStop"
};

private string[] _excludeBrowsableEvents = {
    "AutoSizeChanged",
    "AutoValidateChanged",
    "BindingContextChanged",
    "CausesValidationChanged",
    "ChangeUICues",
    "ImeModeChanged",
    "RightToLeftChanged",
    "Scroll",
    "TabIndexChanged",
    "TabStopChanged",
    "Validated",
    "Validating"
};

private PropertyDescriptorCollection FilterProperties(PropertyDescriptorCollection originalCollection) {
    // Create an enumerator containing only the properties that are not in the provided list of property names
    // and fill an array with those selected properties
    IEnumerable<PropertyDescriptor> selectedProperties = originalCollection.OfType<PropertyDescriptor>().Where(p => !_excludeBrowsableProperties.Contains(p.Name));
    PropertyDescriptor[] descriptors = selectedProperties.ToArray();

    // Return a PropertyDescriptorCollection containing only the filtered descriptors
    PropertyDescriptorCollection newCollection = new PropertyDescriptorCollection(descriptors);
    return newCollection;
}

private EventDescriptorCollection FilterEvents(EventDescriptorCollection origEvents) {
    // Create an enumerator containing only the events that are not in the provided list of event names
    // and fill an array with those selected events
    IEnumerable<EventDescriptor> selectedEvents = origEvents.OfType<EventDescriptor>().Where(e => !_excludeBrowsableEvents.Contains(e.Name));
    EventDescriptor[] descriptors = selectedEvents.ToArray();

    // Return an EventDescriptorCollection containing only the filtered descriptors
    EventDescriptorCollection newCollection = new EventDescriptorCollection(descriptors);
    return newCollection;
}

The above example filters the properties and events based on their name. However, it would not take much effort to filter them on any other criteria, for example the existance and/or value of an attribute, or the property type.

Points of Interest

This technique not only removes access to the properties from the VS designer, but also from the editor and compiler. This means that it will not produce any designer generated code for these properties.

The downside of this is that if the control is placed on a form, and then a property is excluded that the designer has already generated code for, then the whole form will become invalid. To fix that, you need to go into the form.designer.cs module and remove any references to the offending property. I discovered this the hard way by excluding the TabIndex property.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)