
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.
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.
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);
}
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);
}
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.
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) {
IEnumerable<PropertyDescriptor> selectedProperties = originalCollection.OfType<PropertyDescriptor>().Where(p => !_excludeBrowsableProperties.Contains(p.Name));
PropertyDescriptor[] descriptors = selectedProperties.ToArray();
PropertyDescriptorCollection newCollection = new PropertyDescriptorCollection(descriptors);
return newCollection;
}
private EventDescriptorCollection FilterEvents(EventDescriptorCollection origEvents) {
IEnumerable<EventDescriptor> selectedEvents = origEvents.OfType<EventDescriptor>().Where(e => !_excludeBrowsableEvents.Contains(e.Name));
EventDescriptor[] descriptors = selectedEvents.ToArray();
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.
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.