If you're like me and you started coding in VB.NET (or C#, or probably any other programming language) without ever having coded in VB6 then you've probably never felt the need for a control array, at least in the VB6 sense. To hear many migrating VB6 developers tell it though, VB6 control arrays are the greatest programming invention ever and living without them in VB.NET is nigh on unbearable.
The thing is, design-time support for control arrays was added to VB6 for a reason: to enable the same event to be handled for multiple controls with a single method. In VB.NET, you can do that simply by adding multiple controls to the Handles clause of a method. As such, design-time support for control arrays is not needed in VB.NET, so it was never added. Take away the need for handling events for multiple controls and control arrays are exactly like any other arrays, which is exactly how they are treated in VB.NET and all other .NET languages.
VB6 developers often contend that creating a control array in the designer is sooooo much more convenient than doing so in code. I don't necessarily disagree that it could be more convenient but I probably do disagree with how much more. With Intellisense you can generally create an array of controls in code in less than a minute, if not in seconds. If the array should contain a very large number of controls then you can always use a loop and get each control from the form's Controls collection by name. It's really not a big deal.
Anyway, rather than fight it any longer I decided to create something that would help these poor souls. I know that various people have posted various such things around the place. I haven't checked any of those out so I don't know they compare to this current implementation. I've also never used VB6 so I don't know exactly how this compares to how control arrays work in VB6. What I do know is that this current implementation allows you to set the Index property of a control in the designer to add it to a collection that you can access in code, either using a For Each loop or accessing a specific control by index.
So, the first order of business is design-time support. In order to be able to add something to the Toolbox in VS, it must implement the IComponent interface. You can implement IComponent yourself, but the more usual way is to inherit the Component class. As such, that was the first thing to do here:
vb.net Code:
Public Class ControlArray
Inherits System.ComponentModel.Component
End Class
The next thing to do is to provide the Index property on each control. If you've ever used a ToolTip, you know that this can be done, although you may not know how. The secret is the IExtenderProvider interface, along with a few associated tricks. To implement the interface, you must define the CanExtend method. This method takes an object and returns a Boolean that indicates whether the object can be extended or not:
Public Function CanExtend(ByVal extendee As Object) As Boolean Implements System.ComponentModel.IExtenderProvider.CanExtend
Return TypeOf extendee Is Control
End Function
End Class
That's not enough on its own though. There's two more elements required to make this class useful. First, we need to specify exactly what property eligible controls will extended with. After that, we need to actually provide an implementation for that property. A property is just a convenient way to package together two methods: one that gets a value and one that sets a value. In this case, we implement those as just regular methods:
Public ReadOnly Property Controls As List(Of Control)
Get
Return Me._controls
End Get
End Property
Public Function CanExtend(ByVal extendee As Object) As Boolean Implements System.ComponentModel.IExtenderProvider.CanExtend
Return TypeOf extendee Is Control
End Function
Public Function GetIndex(ByVal control As System.Windows.Forms.Control) As Integer
Return Me.controls.IndexOf(control)
End Function
Public Sub SetIndex(ByVal control As Control, ByVal index As Integer)
Me.controls.Insert(index, control)
End Sub
End Class
That's all the basics in place, but that implementation won't do it. The attached solution includes a much more rigorous implementation that addresses these issues. The ControlArray class included in that solution is actually a base class only, with various derived classes provided for various specific types of controls. The sample application includes a form that contains a ButtonArray and a TextBoxArray. When you run the project, you'll be shown each item in the collection.
In the designer, you can select a Button or a TextBox and you'll find an extra Index property at the bottom of the Properties window. Clearing the property will remove the control from the collection and setting it will add the control to the collection. Note that adding and removing controls will automatically adjust the Indexes of all other controls in the collection. Note also that you can set the Index of a control to a value beyond the size of the collection and it will automatically adjust to add the control to the end of the collection. A very interesting feature of this auto-adjusting behaviour is that you can select multiple controls in the designer and then set the index property to a single value for all of them and they will all be added to the collection with distinct Index values.
A further feature of the attached sample is an extended derived class for the RadioButton control. Any and all methods of the base class that add or remove items in the collection have been declared Overridable. This means that you can override all those members and provide extra processing when items are added and removed. The RadioButtonArray class provided adds and removes a handler on the CheckedChanged event of each item in the collection that will uncheck every other item when one RadioButton is checked. This may sound redundant as it's the default behaviour of RadioButton's anyway, but in this case the behaviour is independent of container. This means that RadioButtons in different containers can all act as a single group. The sample contains three groups of three RadioButtons contained in different Panels to demonstrate this.
Finally, the attached solution was created in VS 2010 so will only be able to be opened in VS 2010 or VB Express 2010. You can add the ControlArray.vb code file to any project in VB 2005 or later though. The one change that might be required is the addition of a few line continuation characters.