.NET includes a number of different
UITypeEditors such the
StringCollectionEditor used to edit
Listbox initial contents or any property defined as a
Collection(of String). There is also a nice
ControlsCollectionEditor which allows the user to add new controls to your Component. This article will show you how to implement a
UITypeEditor which allows you to select from controls already on the form.
It has always been more than a little mysterious how to get a list of existing form controls for a
UITypeEditor (which many refer to in shorthand as a
UIDesigner). Mike-MadBadger worked out the hard part for this in his article 'Accessing the Controls on a Form at Design Time'. It is an excellent tip dealing in some detail the basics of a
UITypeEditor works. I will not be re-hashing those details here as it is a short, well written article.
As he points out, he drew heavily from a 2007 article by Saeed Serpooshan. Saeed's article is more in depth but an excellent
UIDesigner primer. Mike took Saeed's work showing how to implement a
UITypeEditor, made it a set of generic procedures in an
MustInherit in VB) class and then replaced the standard drop down editor with a very nice Dialog Form.
The two pieces make an excellent starting point for a highly reusable
ControlsCollectionEditor, which is what I will present here.
First, this version is intended for use only by Components. For instance, you are writing an
ToolTip) which inserts a new property onto various controls. It is not intended for use by Container Controls, for instance if your project inherits from
Panel, in this case you should be able to just drag/drop the desired controls.
One major issue is that certain common controls seem to make their way into the form's controls collection even though they are components. The
UIDesigner actually has access to the components on the form which seems confusing. Since the
UIDesigner is attached to a property defined as
Collection(Of Control) why are there
Components in it? Well, .NET doesn't actually pass the backing field or Collection to the Designer Code. Mike got the form's
Components through some properties associated with the instance passed to the Designer. As such, all that is available is a list of your container's components (
This is great, but trying to add
Components to a
Collection(Of Control) will end badly. One thing I immediately encountered was the
DataGridViewColumn. Since it will only work with/attach to a
DataGridView it is unlikely anyone actually wants to work with it in this context, and second, it is actually a component, not a control.
Another possibility, is the
TabPage. It is possible that someone might be developing a component to work with these, but they are also a specialty control which can only be added to a
TabControl. And there are things like the
TableLayoutPanel - since this is invisible, it seems highly likely that no one will ever want it in their list of controls. So there are a great many more cases where we would want to exclude different types of controls from our
In the course of developing an
UnDoManager component, I set out to again use the Mike-Saeed
ControlsCollectionEditor to allow the developer to select the controls to be managed. Since not all controls interact with the user (like
GroupBox), they are not supported. A final issue is that the parent form is included in the list. That might be fine in some cases, but not mine. So a means to filter controls was needed. As this was the 3rd time I would rework the class, I decided to fix it for good.
So among other things, I added a selection or filtering mechanism to their work so the developer can either select the control types allowed or exclude certain types.
Mike reworked Saaed's class to act as an
MustInherit class, then built his
ControlsCollectionUIEditor on it. It works great as a generic demo, but as shown, in the real world things are often more complex. So, I did the same thing as Mike: I made some small changes to his class and made it
MustInherit and will show you how to use it as a Base Class. This will require very little code to implement.
No Forms Allowed
First, a flag was added to
ExcludeForm(s) from the control list. Since Forms inherit from Control, they look like just another control and can make it onto the list of controls. The exclusion mechanism (described next) could be used for forms as easily as a
TableLayoutPanel, but I also used a
ExcludeForm flag as a convenience for those who just need to exclude the form. Keep in mind that this is for working with a component you are developing, so you should know in detail what it can and cannot deal with.
An Exclusive Club
There was an inherent need to exclude certain things like the
DataGridViewColumn (a component, not a control), the
TableLayoutPanel (invisible) and maybe the
TabPage (can only be added to a
TabControl). So, an exclusion mechanism was clearly needed.
The standard .NET
ControlsCollection editor has an inclusion mechanism and that's what I needed for the
UnDoManager: a way to only include the types of controls my tool was designed to work with. For maximum flexibility, I implemented it both ways: An include
List and an exclude
List. This way, you use either one depending on whether you need to let in, or keep out just a few types.
Just before Mike's dialog form displays, Saaed's base class will call
LoadValues. Here is how the filters are applied:
Dim bAdd As Boolean = True
Dim thisCtl As Control = Nothing
For Each obj As Object In context.Container.Components
bAdd = True
If TypeOf obj Is Control Then
thisCtl = CType(obj, Control)
If ExcludeForm Then
bAdd = Not (TypeOf thisCtl Is Form)
If (typeIncludeOnly IsNot Nothing) AndAlso (typeIncludeOnly.Count > 0) Then
If typeIncludeOnly.Contains(thisCtl.GetType) = False Then
bAdd = False
If (typeExclude IsNot Nothing) AndAlso (typeExclude.Count > 0) Then
If typeExclude.Contains(thisCtl.GetType) Then
bAdd = False
If bAdd Then
These various settings I've added are just
Protected Friend variables. The lists are already instanced, so there is nothing for you to do except add your Types to them. Code such as:
If (typeExclude IsNot Nothing) Then
tries to watch for someone who thinks it is a good idea to set it to Nothing just in case there is something in it (there are no defaults). Otherwise, I left
Try/Catch blocks out of it because this is a design time tool only, and if you are using it wrong it seems best to let the Exceptions through so you know. Catching them in a design time tool such as this actually makes them harder to find (there was one in Saaed's code which was very hard to find).
Other Small Changes
One of the things I liked best about Mike's work was learning how to use a modal dialog form instead of the default dropdowns. However, for maximum flexibility, I implemented a second class to use the dropdown method. These are small and terribly ugly with .NET appending the full type name and other "useful" information to the Control Name. That said, there are cases where that method might be more appealing.
The result is that there are now two classes:
ControlCollectionDropDownUIEditor for invoking the dropdown
ControlCollectionDialogUIEditor for the Dialog Form version. There are other classes, but they are
MustInherit base classes.
Most other changes were to the structure, such as making it into a DLL to prevent loosing files or accidentally changing something critical. To use the DLL form, add a reference and import the namespace. If you do choose the file method, the dialog form was merged into the ControlCollectionEditor.vb file so there is one less file to keep track of.
What it Looks Like
Selecting the ellipsis for the controls property displays the Dialog Form. In this case, we exclude
GroupBox from the eligible list.
The .NET default
DropDown version follows the same rules.
Using the Code
Using the included demo, here is what is required to implement the
ControlCollectionUIEditor. (the demo doesn't DO anything but provide a host for an
ExampleComponent to use in VS). You will very likely need to Clean and Build the project since VS needs a compiled version of the project to implement the
ExampleComponent in the project.
1. Decorate the property which will be implementing a Collection(Of control) with the EditorAttribute:
Private _TargetControls As New Collection(Of Control)
Public Property TargetControls() As Collection(Of Control)
- Note that
System.Collections.ObjectModel, not the
- The designer name is one you create for your
Class property: in this case,
- This has nothing to do with the
UIEditor, but you will also need these procedures for your
Component to work correctly:
Public Sub ResetTargetControls()
_TgtControls = Nothing
Public Function ShouldSerializeTargetControls() As Boolean
Return (_TgtControls IsNot Nothing)
Note how your property name is embedded into the procedure names.
2. Write the UIEditor
Fully 99% of the work is already done and should be in the DLL, you just need to provide a local class. This class can be in the same file as your main project (your property must be decorated as above):
System.Security.Permissions.SecurityAction.Demand, Name:="FullTrust")> _
Public Class ExampleFormControlCollectionUIEditor
Public Sub New()
MyBase.ExcludeForm = True
Note: It is hard to imagine a case using both the inclusion and exclusion list. Use whichever one fits your use case best. Both are shown above to illustrate the names and usage.
- The class name (
ExampleFormControlCollectionUIEditor) is exactly the same one used in the property
typeExcludeOnly are a
- There are NO DEFAULT entries for either one. I debated adding
TableLayoutPanel and maybe
TabPage as default exclusions, but decided that was bad since you can't easily see what is in there. Also, having used this 4 times now, it seems including only certain types is by far the more common use case.
- If you leave the
typeIncludeOnly empty (
Count == 0), all the controls on the form will show in the list. When Types are added, this sort of works like the
CanExtend function in an
- As noted, the form itself can be excluded either using the
ExcludeForm flag or by adding
Form to the
typeExcludeList. The flag is kind of nice if that is all you need to modify as to controls.
- If you do not need to tweak any settings, you will just need to call
To test this, you must clean and build the demo. Then in design mode, open the form, select the component in the form tray, then in the Properties window, select '
TargetContols'. That's it. Saeed's work takes over to implement the .NET collection editor using Mike's Dialog Form.
The demo has an example component and BOTH the
DropDown versions coded. To test the
- Change the
EditorAttribute on your property to
- Clean and build so VS can compile and use the other editor in the Properties window
Class, Member Reference
ControlCollectionDialogUIEditor MustInherit Class
Allows the developer to select existing Form Controls using a modal dialog. Uses a
CheckedListBox so multiple controls can be selected.
ControlCollectionDropDownUIEditor MustInherit Class
CheckedListBox to allow the developer to select multiple Form Controls.
A list of
System.Types (controls). Only controls of the Types in the list will show on the
A list of
(controls). All controls of these Types will be excluded from the
Flag indicating whether to include this component's parent form in the list. Default is
Applies only to the
ControlCollectionDropDownUIEditor. Sets the width of the drop down. The minimum is 280, default value is 400.
In addition to recompiling to make a NET 4.0 version, the update includes a smart
UIEnumEditor. This will automatically use the right control for flag/bitwise
Enum properties, and detect and use descriptions if present.
.NET natively handles properties declared as
Enums quite well - unless it is a bitwise or flag type
Public Enum FlagColors
None = 0
Red = 1
White = 2
Blue = 4
Green = 8
Yellow = 16
The default NET
UIEditor seems to ignore or be unaware of the FlagsAttribute and uses a single select
ListBox in the Properties window, which does not allow multiples to be selected as they should. The
UIEnumEditor provides a dropdown
CheckedListBox for selecting multiple items:
Public Property EnumOfFooExample As Foo
I routinely associate descriptions with
Enums when they will be displayed to the use in a Combo or List box, and sometimes I like these descriptions to be used in the IDE Property panel.
Public Enum Stooges
<Description("Larry - Funny one")> Larry
<Description("Moe - 'Smart' One")> Moe
<Description("Curly - Sore One")> Curly
<Description("Shemp - One with bad haircut")> Shemp
<Description("CurlyJoe - Last one")> CurlyJoe
Public Property EnumOfStooge As Stooges
However, I don't like having to have specify a different
UIEditor based on the nature of the underlying Enum. This
UIEnumEditor is intended to be smart and take the appropriate action based on the
Enums will automatically use the
Description text if available.
- Any members missing a
Description will use the
- A simple
Enum property with no descriptions will look/act the same as the default .NET.
Enums, will automatically use a
CheckedListBox dropdown with the
- To work properly, the
Enum needs the
FlagsAttribute and the values must actually be bitwise values (0, 1, 2, 4, 8, 16, 32, 64 ...)
- These will never show the Zero value member. This value should represent None, which is indicated by selecting no members.
You can subclass the editor to change the default behavior. For instance, to tell it to display the
Descriptions for a flag
Public Class EnumFlagFruitEditor
Public Sub New()
Me.UseDescription = True Me.ControlWidth = 280
Since the design time properties are used by developers, not end users, it seems best (to me) to use the
Enum Names for bitwise properties to make clear the combination being created. So the default behavior is not to use descriptions with these. To override this, set the
UseDescription property to
Likewise, you can force simple/non bitwise
Enums to ignore the description by subclassing and setting
false (but this is the default NET behavior).
Both controls are
Sizeable, but you can set the initial dropdown width using
The demo illustrates several combinations of Flag and non-Flag
Enum properties. However, it is difficult to tell what is being illustrated without looking at the code (see EnumCtl.vb). The demo uses the same default
UIEnumEditor for both normal and flag style
Enums both with and without descriptions associated with them.
Developing tools which will run in the developer's VS designer can be confusing: you are after all using Visual Studio to write something which will run in VS at design time. It is not something many of us do everyday and there are not a lot of clear references on the subject.
Saeed's article on this is informative and can be very helpful in understanding some of the arcane aspects of UI Designers in general. Mike's article is equally valuable, abstracting Saaed's work into a set of base tools and adding a dialog form to the toolset.
The object of the code provided here was to use these previous efforts to provide the means to implement a flexible and controls collection editor powerful enough to handle a variety of situations.
- Initial article and ver 1.02 of the sample code
- Updated .NET 4 version
- Added a small
- Addendum explaining the same