OK, so in the mean time I've had lunch and written the editor myself. Wasn't such a great effort. I'd be glad to share, if it helps.
Edit: Solution Summary
I wrote an editor (see screenshot) which recursively lists all the controls found on the form in a treeview. If the controls have their Text or Image properties set the text/image is displayed. The user can select multiple controls with checkboxes and even filter the list to only display controls of certain types.
(I must admit, though, that since this editor was only intended for internal use, I didn't bother to check for the image property generically, I just handle a couple of well-known control types.)
The code in the editor form is really just for the UI, responsible for filling the tree, setting the checks on the list of pre-selected controls and returning the list of selected controls when the user closes the form with OK.
Next, we have a class that implements UITypeEditor, which I called FormControlSelectionEditor. This class is assigned to the properties which we want to use the editor for using the [Editor] attribute. It doesn't do much more than create a new instance of the form when required and display it as a modal dialog.
Then there are the properties themselves, which are of type System.Collections.ObjectModel.ObservableCollection(Of Control). I chose ObservableCollection because I need to react to changes to the lists at run-time as well, but other lists would do just as well with minor adaptation.
One thing I discovered is that I had to write my properties and editor such that they use copies of the lists of controls. In other words, the UITypeEditor code makes a copy of the list stored in the property and passes it to the editor form (for setting the checks when the form is opened), and when the form is closed, I clear the property's backing list and copy over each control in the list returned from the editor. I found that otherwise I had problems with serialization in the .designer file. I don't believe it has to be this way; I think it's more likely an error on my part.
Code for a typical property:
Private WithEvents _insertButtons As New System.Collections.ObjectModel.ObservableCollection(Of Control)
<DesignerSerializationVisibility(DesignerSerializationVisibility.Content)> _
<Editor(GetType(Design.FormControlSelectionEditor), GetType(UITypeEditor))> _
Public Property InsertButtons() As System.Collections.ObjectModel.ObservableCollection(Of Control)
Get
Return _insertButtons
End Get
Set(ByVal value As System.Collections.ObjectModel.ObservableCollection(Of Control))
If value Is Nothing Then
RemoveInsertButtonEventHandlers(_insertButtons)
_insertButtons.Clear()
Else
' Copy the list items passed in into the internal list
For i As Integer = _insertButtons.Count - 1 To 0 Step -1
If Not value.Contains(_insertButtons.Item(i)) Then _insertButtons.RemoveAt(i)
Next
For Each ctl As Control In value
If Not _insertButtons.Contains(ctl) Then _insertButtons.Add(ctl)
Next
End If
End Set
End Property
Private Function ShouldSerializeInsertButtons() As Boolean
Return _insertButtons.Count > 0
End Function
Private Sub ResetInsertButtons()
InsertButtons = Nothing
End Sub
I've put the editor into a zip file; download it here.
As I mentioned before, this was just a quick & dirty solution intended for internal use only. All the same, I'd appreciate any suggestions for improvement.
Control
. I'm trying to achieve the same functionality with a User Control via codeproject.com/Articles/687430/… that drew from your work. I get a cast error : Cannot cast object [UserControl] to [Control] :( – Fidelafidelas