Xtreme Visual Basic Talk

Xtreme Visual Basic Talk (http://www.xtremevbtalk.com/)
-   Tutors' Corner (http://www.xtremevbtalk.com/tutors-corner/)
-   -   Control Arrays in .NET (http://www.xtremevbtalk.com/tutors-corner/174835-control-arrays-net.html)

Iceplug 06-24-2004 05:10 PM

Control Arrays in .NET
 
After messing around in VB.NET, one might notice that there are no Index properties on the controls.
Incorrect Snap Conclusion one might make:
Oh... I can't make a control array!!! I can't make a list/grid of controls! No!!!

But... there are lots of ways to create a control array in .NET.

Under the hood of your form class
If you have ever spent some time looking in the Windows Forms Designer Code #region, you might notice the code required to actually set up a control on the form, in the case of a button, you will find the declaration, which is declared in the form's Declarations section:
Code:

    Friend WithEvents Button1 As System.Windows.Forms.Button
Next, instantiating the button:
Code:

        Me.Button1 = New System.Windows.Forms.Button
Then, there is the setting of the properties:
Code:

        Me.Button1.Location = New System.Drawing.Point(136, 96)
        Me.Button1.Name = "Button1"
        Me.Button1.Size = New System.Drawing.Size(88, 16)
        Me.Button1.TabIndex = 0
        Me.Button1.Text = "Button1"

Then, the button is added to the form.
Code:

        Me.Controls.Add(Me.Button1)
I can do that too!
OK, surely, we should be able to do something just like this for an array of controls. I mean, a For Loop or two should accomplish this same thing for an array. Let's try it.

Declaration in the form:
Code:

    Friend WithEvents Buttons(99) As Button
Drat... an error! WithEvents cannot be typed as arrays... so, we can't use WithEvents on an array declaration.
Ah... frustrated, let's get rid of it. ;)
Code:

    Friend Buttons(99) As Button
Another incorrect snap conclusion: So much for actually having an event handlers for this array.
We'll discuss adding events later. Back to our Button Friends.

There is the instantion, the setting of the properties, and the form getting the button added onto it; let's do all of those in the For loop:
Code:

    Dim LV As Integer
        For LV = 0 To 99

            Buttons(LV) = New Button  'Create the button.
'It has a default width and height.
            Buttons(LV).Left = Buttons(LV).Width * (LV Mod 10)
            Buttons(LV).Top = Buttons(LV).Height * (LV \ 10)
            Buttons(LV).Name = "Btn" & LV
            Buttons(LV).Text = LV.ToString()

            Me.Controls.Add(Buttons(LV))

        Next

Ahh... that looks decent.
And... it works! :D Yay!
Control arrays! *Dance*
OK, now, why can't I get any events to my array?

Yes, you can get events for the buttons in the array.
Code:

    Public Sub ButtonClickEventProc(ByVal sender As Object, ByVal e As EventArgs)
    'I want this event to run for all of the controls.
    End Sub

Now, using Handles at the end of this subroutine doesn't work... :(
You'd only get controls declared WithEvents, and since we can't use WithEvents on the control array... how can we possibly add an event to the control? :confused:

... AddHandler.

Taking a look at event handling at runtime should popup two keywords: AddHandler and RemoveHandler... since we don't need to remove any events, AddHandler looks promising.

Let's AddHandler
So, in our For Loop, we can Add Handlers to the buttons in the array.
Code:

            AddHandler Buttons(LV).Click, AddressOf ButtonClickEventProc
And now, we have an event set up for the control array!

Addenda:
  • You can add a one-dimensional array of controls to the form by using Me.Controls.AddRange
  • You can modify the button that you clicked on by DirectCasting the sender into a button: Dim btn As Button = DirectCast(sender, Button)
  • You can have a multi-dimensional array of controls as well.
  • You can add a control to the control array by Declaring another button instance and doing the above to create a new control.
  • You can handle any event.
  • Your event handler procedure must have the right signature... which means the sub must have (sender As Object, e As ???), where ??? is some brand of event args (for a click, it is just EventArgs, and for a mouse event, it is MouseEventArgs)

So, here is a form with a button control array. (As a .vb file)
Code:

Imports System
Imports System.Windows.Forms

Public Class Controlland
    Inherits System.Windows.Forms.Form

    Friend Buttons(99) As Button
    'Declare the buttons.

    Public Sub New() 'This is the Sub New, the constructor, etc.

        MyBase.New()  'Initialize the form itself.

        CreateArray()

    End Sub

    Public Sub CreateArray() 'This subroutine initializes the array.

        'This will be done in a For Loop
        Dim LV As Integer  'LV is our loop variable.

        For LV = 0 To 99 'Set up the loop to go through each button in the array.

            Buttons(LV) = New Button  'Create the button.
            'This creates the button at the top left of the form with the default size.

            Buttons(LV).Left = Buttons(LV).Width * (LV Mod 10)
            Buttons(LV).Top = Buttons(LV).Height * (LV \ 10)
            'Initializes the button locations.

            Buttons(LV).Name = "Btn" & LV
            Buttons(LV).Text = LV.ToString()

            'Add an event handler and whatever you may want to add.
            AddHandler Buttons(LV).Click, AddressOf ButtonClickEventProc
            'This event handler handles the click event of all of the buttons.

        Next

        Me.Controls.AddRange(Buttons)
        'Add... ALLLL of the buttons to the form.

    End Sub


    Public Sub ButtonClickEventProc(ByVal sender As Object, ByVal e As EventArgs)
        'Button control array event handler.
        'sender has to be recast as a Button
        Dim Btn As Button = DirectCast(sender, Button)
        Btn.Enabled = False

    End Sub

    Public Shared Sub Main()
        Dim F As Controlland = New Controlland
        F.ShowDialog()
    End Sub


End Class


Iceplug 07-26-2004 06:26 PM

Two of my readers have posed interesting questions about increasing the functionality of the control array that we have created here, and I will demonstrate them here as well.

How to add buttons to the control array at runtime?
The solution requires us to modify our original idea of using a fixed array for the control array. The idea is that our control array will have a dynamic size and grow larger during runtime.

We *could* do this with a dynamic array in .NET, but dynamic array resizing is a very VB specific feature, not a .NET feature.
While one can do:
Code:

Dim X() As Integer
'and then
X = New Integer (10) {}   
'or
X = New Integer () {10, 20, 30, 40, 50, 60, 70, 80, 90, 100}

Both of those methods are fine. The non-NET culprit is the Redim statement, which is not .NET.

For our control array, we cannot use the first option
Buttons = New Button (10) {}
This is equivalent to Redim without Preserve. We get a new empty array.
Buttons = New Button () {A, B, C, D, E, F, G, H, I, J}
And that would be ridiculous (it also requires that we know the size of the array at design-time.

Our solution: ArrayList.
If you have never used the ArrayList, you should try it. It's a wonderful way to store unknown numbers of items... and it's very similar to a VB6 Collection, if you are familiar with VB6, except with more power.

So, we redesign our control array for use with an ArrayList.
First, we declare our Arraylist.
Code:

    Public Sub CreateArray() 'This subroutine initializes the array.
        Friend Buttons As ArrayList

Now, there is the traditional setting-of-properties subroutine that we must redesign :).
Code:


        Buttons = New ArrayList(67)  'Creates the arraylist.  The number 67 isn't very important.
        'But you should keep it as close to the maximum as possible.

This instantiates the arraylist. The number that you supply as the capacity isn't important (the capacity will increase itself if you go over), but you should generally try to keep the capacity as close to the maximum value without going over. If you don't know if your array is going to have a maximum, then just pick a number and move on. :D

Of course we have to initialize the buttons.
We can do this in a For Loop again.
It's very similar to how we did it in part one.
Our Btn As Button is a representative for each button that we add to the control array.

Code:

        Dim LV As Integer
        Dim Btn As Button
        For LV = 0 To 49
            'The property setting ceremony.
            Btn = New Button
            Btn.Left = Btn.Width * (LV Mod 10)
            Btn.Top = Btn.Height * (LV \ 10)
            Btn.Text = "???"
            AddHandler Btn.Click, New EventHandler(AddressOf WhenClicked)

            Me.Controls.Add(Btn)
            Buttons.Add(Btn)
        Next

    End Sub

Notice that we are adding the Btn to the form's controls AND the Buttons arraylist.

Our event procedure:
Code:

    Private Sub WhenClicked(ByVal sender As Object, ByVal e As EventArgs)
        Dim Btn As Button = DirectCast(sender, Button)

        Dim RelIndex As Integer = Buttons.IndexOf(sender)
        Btn.Text = RelIndex.ToString()

        'That is a handy way of determining the 'Index' of your control array item if you so desire.
        'The LBound is always zero due to the Arraylist and .NET in General.
        'The UBound is found as Buttons.Count - 1

    End Sub

The Index of the Button Clicked
In case you were wondering how to get the index of the control that you clicked:
Dim RelIndex As Integer = Array.IndexOf(Buttons, sender)
And RelIndex contains the index of the item that the user clicks.


Now that all of this has been setup:
How to add buttons to the control array at runtime?

The answer lies here: We have created the arraylist and now, since you know the arraylist has a dynamic size, adding buttons to the form should be a piece of cake... and it is.

Add this to the CreateArray subroutine.
Code:

        'Click the form to add a new button to the array.
        AddHandler Me.Click, New EventHandler(AddressOf FormClicked)

And now to add a button when you click the form:
Code:

    Private Sub FormClicked(ByVal sender As Object, ByVal e As EventArgs)
        Dim Btn As Button = New Button
        'Our new button enlistee :}.
        Dim Endex As Integer = Buttons.Count

        'Set the properties.
        Btn.Left = Btn.Width * (Endex Mod 10)
        Btn.Top = Btn.Height * (Endex \ 10)
        Btn.Text = "???"
        AddHandler Btn.Click, New EventHandler(AddressOf WhenClicked)

        Me.Controls.Add(Btn)
        Buttons.Add(Btn)
    End Sub

Really, a new Button is just instantied and its properties are set. What makes it become a member of the control array is the fact that it is added to the Me.Controls collection and the Buttons arraylist.
Me.Controls.Add(Btn)
Buttons.Add(Btn)

The controls index is simply Buttons.Count, assigned to Endex (the End index).
The index for our new button is not entirely important: you can just add the button directly to Me.controls and Buttons without using the Endex.
It's a handy way to get the index if you want it. (Like if you want the buttons to line up).

But, I hope you learned something interesting from this demonstration.

Addenda:
  • Once again you can have any type of control instead of Button for the control array.
  • You cannot have a multi-dimensional arraylist, unless you store arrays in the arraylist.
  • You can handle any event.
  • The fourth addendum in the previous post is a misnomer... the control created is not in the array.
So, here's our dynamic control array.
Code:

Imports System
Imports System.Collections
Imports System.Windows.Forms

Public Class Controlland
    Inherits System.Windows.Forms.Form

    Dim Buttons As ArrayList
    'Declare the buttons.

    Public Sub New() 'This is the Sub New, the constructor, etc.

        MyBase.New()  'Initialize the form itself.

        CreateArray()

    End Sub

    Public Sub CreateArray() 'This subroutine initializes the array.
        Dim LV As Integer
        Dim Btn As Button
        Buttons = New ArrayList(67)  'Creates the arraylist.  The number 67 isn't very important.
        'But you should keep it as close to the maximum as possible.

        For LV = 0 To 49
            'The property setting ceremony.
            Btn = New Button
            Btn.Left = Btn.Width * (LV Mod 10)
            Btn.Top = Btn.Height * (LV \ 10)
            Btn.Text = "???"
            AddHandler Btn.Click, New EventHandler(AddressOf WhenClicked)

            Buttons.Add(Btn)
            Me.Controls.Add(Btn)
        Next

        'Click the form to add a new button to the array.
        AddHandler Me.Click, New EventHandler(AddressOf FormClicked)

    End Sub


    Private Sub WhenClicked(ByVal sender As Object, ByVal e As EventArgs)
        Dim Btn As Button = DirectCast(sender, Button)

        Dim RelIndex As Integer = Buttons.IndexOf(sender)
        Btn.Text = RelIndex.ToString()

        'That is a handy way of determining the 'Index' of your control array item if you so desire.
        'The LBound is always zero due to the Arraylist and .NET in General.
        'The UBound is found as Buttons.Count - 1

    End Sub

    Private Sub FormClicked(ByVal sender As Object, ByVal e As EventArgs)
        Dim Btn As Button = New Button
        'Our new button enlistee :}.
        Dim Endex As Integer = Buttons.Count

        'Set the properties.
        Btn.Left = Btn.Width * (Endex Mod 10)
        Btn.Top = Btn.Height * (Endex \ 10)
        Btn.Text = "???"
        AddHandler Btn.Click, New EventHandler(AddressOf WhenClicked)

        Me.Controls.Add(Btn)
        Buttons.Add(Btn)
    End Sub

    Public Shared Sub Main()
        Dim F As Controlland = New Controlland
        F.ShowDialog()
    End Sub


End Class

*Color syntaxed for your enjoyment.


All times are GMT -6. The time now is 05:40 AM.

Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2017, vBulletin Solutions, Inc.
Search Engine Optimisation provided by DragonByte SEO v2.0.15 (Lite) - vBulletin Mods & Addons Copyright © 2017 DragonByte Technologies Ltd.
All site content is protected by the Digital Millenium Act of 1998. Copyright©2001-2011 MAS Media Inc. and Extreme Visual Basic Forum. All rights reserved.
You may not copy or reproduce any portion of this site without written consent.