Click here to Skip to main content
15,880,651 members
Articles / Programming Languages / C#

Remove Unwanted Properties and Events from UserControl

Rate me:
Please Sign up or sign in to vote.
4.87/5 (16 votes)
14 Oct 2014CPOL2 min read 24.2K   23   11
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)


Written By
Software Developer
Australia Australia
Been programming for 40 years now, starting when I was 13 on DEC PDP 11 (back in the day of paper tape storage, and hex switch boot procedures). Got right into micro-computers from an early age, with machines like the Dick Smith Sorcerer and the CompuColor II. Started CP/M and MS-DOS programming in the mid 1980's. By the end of the '80's, I was just starting to get a good grip on OOP (Had Zortech C++ V1.0).

Got into ATL and COM programming early 2002. As a result, my gutter vocabulary has expanded, but it certainly keeps me off the streets.

Recently, I have had to stop working full time as a programmer due to permanent brain damage as a result of a tumour (I just can't keep up the pace required to meet KPI's). I still like to keep my hand in it, though, and will probably post more articles here as I discover various tricky things.

Comments and Discussions

 
GeneralMy vote of 5 Pin
Maciej Los13-May-20 2:48
mveMaciej Los13-May-20 2:48 
QuestionYou could use nameof Pin
Philippe Mori10-Apr-17 13:39
Philippe Mori10-Apr-17 13:39 
QuestionPossible in VB? Pin
rx7man15-Oct-14 10:52
rx7man15-Oct-14 10:52 
AnswerRe: Possible in VB? Pin
Midi_Mick15-Oct-14 14:18
professionalMidi_Mick15-Oct-14 14:18 
GeneralRe: Possible in VB? Pin
rx7man15-Oct-14 15:58
rx7man15-Oct-14 15:58 
Questionthanks for sharing! Pin
Member 373058715-Oct-14 5:29
Member 373058715-Oct-14 5:29 
Questionhave you consider to post this as a tip? Pin
Nelek14-Oct-14 21:50
protectorNelek14-Oct-14 21:50 
AnswerRe: have you consider to post this as a tip? Pin
Midi_Mick15-Oct-14 0:14
professionalMidi_Mick15-Oct-14 0:14 
GeneralRe: have you consider to post this as a tip? Pin
Nelek15-Oct-14 10:06
protectorNelek15-Oct-14 10:06 
Midi_Mick wrote:
due to the complexity of the concepts (although the implementation itself is relatively simple)

It is a good point.

I am not a policeman, I just try to make the posters to think about. Many new users don't even know about the differences.

Midi_Mick wrote:
it was just the decision I made at the time.

You seem to have considered both views. The decission is O.K. for me.
M.D.V. Wink | ;)

If something has a solution... Why do we have to worry about?. If it has no solution... For what reason do we have to worry about?
Help me to understand what I'm saying, and I'll explain it better to you
Rating helpful answers is nice, but saying thanks can be even nicer.

Generalit is very useful Pin
Southmountain14-Oct-14 8:28
Southmountain14-Oct-14 8:28 
GeneralRe: it is very useful Pin
Midi_Mick14-Oct-14 14:03
professionalMidi_Mick14-Oct-14 14:03 

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

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