Introduction
During my interactions in the C# Forum, I came across questions on providing Design time support for custom controls. So I thought of sharing a little experience I have on this.
The Design time support architecture allows us to develop customized design time extensions using which we can configure our controls/properties of the control. Ex: Dock
, Anchor
properties of Button and other controls. I also encountered a similar scenario while developing a custom user control, where I needed to provide User Interface for modifying its property at design time.
Let's see in brief the .NET FrameWork support for this.
.NET Support
The .NET FrameWork has interfaces that we can use to implement customized design time support. There are three ways in which we can provide this.
UITypeEditor
TypeConverter
s
- Designers
UITypeEditors:
UITypeEditor
s allow us to provide a custom user interface for editing the value of a property of the control. This also allows us to display the visual representation of the current value in the properties window.
TypeConverters:
TypeConverter
s allow us to convert the values between the types. They also allows us to provide logic for configuration of a property in the property browser.
Designers:
Designers allow us to customize the behaviour of our control at design time.
This article explains how we can use UITypeEditor
s and TypeConverter
s, to provide design time support. Using Designers are explained in the next article.
Using UITypeEditor/Property Editor
Dock
, Anchor
properties of .NET controls provide user interface for supplying/setting their value. These are called as Property Editors. We can use .NET FrameWork UITypeEditor
class to create such interfaces for our custom properties. Property Editors are generally displayed in the drop-down area inside the properties window itself. But we can also make them to appear as modal windows.
Let's see how we can create Property Editors for our custom properties. We will use a custom control (Rectangle
Control) for explaining this. Rectangle Control draw a filled rectangle. This control exposes two custom properties, RectWidth
and RectHeight
. Users can use these properties for setting width and height of the rectangle.
It will be more useful if we can provide a user interface (TrackBar
) for supplying these values instead of asking the users to enter the values.
Fig 1: Property Editor for Setting RectHeight
and RectWidth
The first step in creating the property editor is to specify the editor (WidthEditor
) in the Editor
attribute associated with the property procedure.
[Description("Width of the rectangle"),Editor(typeof(WidthEditor),
typeof(UITypeEditor))]
public int RectWidth
{
get{
return this.width;
}
set{
if( value > 0){
this.width = value;
this.Invalidate();
}
}
}
The Editor
attribute takes two System.Type
arguments. The first argument is the type of editor. (WidthEditor
- we will be creating this in the second step). The second argument is always typeof(UITypeEditor)
.
In the second step we have to create WidthEditor
class. This class should be inherited from System.Drawing.Design.UITypeEditor
class and must override two methods. They are GetEditStyle()
and EditValue()
methods.
The form designer calls GetEditStyle()
method when it's filling the properties window with the values of all the properties of the control. This method returns an enumerated value that tells the designer whether the property editor is going to display in the drop-down area or modal form. In our example the property editor is displayed in the drop-down area.
public override UITypeEditorEditStyle
GetEditStyle(System.ComponentModel.ITypeDescriptorContext
context)
{
return
UITypeEditorEditStyle.DropDown;
}
The form designer calls the EditValue()
method when the user clicks the button beside the property name. The EditValue()
method creates TrackBar
control and sets the configuration details for it. (Like Range, Orientation). This method uses IWindowsFormEditorService
class object to show the TrackBar
control in the drop-down area.
IWindowsFormsEditorService frmsvr =
(IWindowsFormsEditorService)provider.GetService(
typeof(IWindowsFormsEditorService));
frmsvr.DropDownControl(tbr);
bar
That's all we need to do. Now we can use this control by adding it to the from designer. (The attached solution has TestCustomControl
project which consumes this control). When we select RectWidth
or RectHeight
properties, a TrackBar
control will be displayed. The values of these properties can be changed using the TrackBar
. When we change these values, the new value will appear in the property browser and rectangle will be redrawn with the current dimension . Very simple. Am I right?
If we want to display the modal form instead of showing it in drop-down area, we need to follow the same steps with only two differences.
- Return value of
GetEditStyle()
should be UITypeEditorEditStyle.Modal
.
We need to create a Form
class and should add all the controls that make up the editor interface to this Form
. In the EditValue()
method we need to create an instance of this form. Now to show this form we need to call ShowModal()
method of IWindowsFormsEditorService
class.
Let's move into the second topic of this article. Using TypeConverter
s.
Using TypeConverters
We know that few properties (Font
, Size
) return objects instead of scalar values and these properties are displayed in the property browser with a "+" sign. If our custom control contain any properties that return .NET FrameWork defined objects (Font
, Point
, Location
, Size
) then they will inherit the similar behaviour. But if our custom control contains any property that returns a custom object, then how can we achieve the similar behaviour? To get that, we need to create a custom TypeConverter
class.
Fig2:PhoneData property with "+" sign
The current example use PhoneNumber
custom control. This control allows the users to enter Name
, CountryCode
, AreaCode
, PhoneNumber
details. Let's expose this data in a single property instead of exposing them in five different properties.
The first step in doing this is to create PhoneNumber
class that wrap all the properties and raise PropertyChanged
event when any property changes. We need to decorate this class with TypeConverter
attribute.
[TypeConverter(typeof(PhoneTypeConverter))]
public class PhoneNumber
{
}
TypeConverter
attribute tells the form designer that PhoneTypeConverter
is associated with PhoneNumber
class.
In the second step we need to create PhoneTypeConverter
class which is derived from System.ComponentModel.TypeConverter
and override two methods. They are GetPropertiesSupported()
, GetProperties()
methods.
GetPropertiesSupported()
method returns whether the object supports properties or not. Form designer use this value to display "+" symbol at PhoneData
property. Since our object supports properties GetPropertiesSupported()
method returns true value.
GetProperties()
method returns PropertiesDescriptorCollection
object which will describe the items that will appear when the "+" symbol is clicked.
public override PropertyDescriptorCollection
GetProperties(ITypeDescriptorContext context,
object value, Attribute[] attributes)
{
return
TypeDescriptor.GetProperties(typeof(PhoneNumber));
}
The third and last step is to set the DesignerSerializationVisibility
attribute of the PhoneData
to Content.
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
DesignerSerializationVisibility
attribute specify the type of persistence to use while serializing a property on a control at design time. Setting this value to content tells VS.NET that, it should serialize the contents of this PhoneData
property.
Now it's the time for compiling and using the control. When we add this control to the form designer the PhoneData
property of the PhoneNumber
control appear with "+" sign in the properties window. Clicking on the "+" sign will expand all the items. When we modify any value it will be displayed in the PhoneNumber
control.
Conclusion
In this article we saw how we can use PropertyEditor
and TypeConverter
s for enhancing design time support of our custom controls. In the next article we will see how we can use designers.