Link to home
Start Free TrialLog in
Avatar of charlieb01
charlieb01

asked on

Dynamically Creating Control Arrays in VB.NET 2003

Hi,

I have seen a lot of questions and solutions here about the issue of not being able to use control arrays in VB.NET the way we used to be able to do it in VB6. After much research and playing around with some examples I found here on Experts-Exchange, I decided I should share what I discovered so everyone can benefit. I am assigning points to this simply because I am a newbie to VB.NET and I would appreciate any feedback that could enhance what I have put together or if someone can determine that there are problems with what I did and are able to offer fresh ideas.

For my application I have a MS Access database table for digital Outputs (used to turn things on and off in the physical world). For each project we build, the number of digital outputs is different. The database table contains multiple fields but the two most important are the 'SensorID' and the "NameLong". The 'SensorID' field is the PK in this table. The 'NameLong' field is the descriptive text of the function of what the checkbox does. For the user to better understand what these do I need to provide both the 'SensorID' and the 'NameLong' as the text for the checkbox. What I needed to do was create a checkbox on a specific tab on a form for each of the digital Outputs in the database table.

At Form Level I have this:
Dim cbx() As CheckBox

I then have the following subroutine in the form code that gets the data from the database table and dynamically creates the checkboxes and sets their properties. Most of the code below should be self explanatory but I have added some comments:

Public Sub SetupICdigOut()
        Dim i As Integer
        ' constr is the connection string for the system database
        ' constr is defined in ProjConst.vb code
        Dim conn As New OleDb.OleDbConnection(constr)
        Dim strSql As String
        strSql = "SELECT SensorID, NameLong, NameShort, SlotNumber, ChannelNumber, OrderNum "
        strSql = strSql & "FROM DIG_OUT_Definition ORDER BY OrderNum"

        Dim da As New OleDb.OleDbDataAdapter(strSql, conn)
        Dim dt As New DataTable
        da.Fill(dt)

        Dim digOutCount As Integer = dt.Rows.Count  'how many dig outputs are there?

        ReDim cbx(digOutCount)  'set array dimension based on the number of Dig Outputs in database table

        i = 0   'cbx(0) to cbx(numOfCheckBoxesNeeded-1)
        For Each dr As DataRow In dt.Rows
            cbx(i) = New CheckBox
            cbx(i).Name = "chkBx_" & CStr(dr.Item("SensorID"))  'give it a name based on the PK in database
            cbx(i).Left = 50                                    'set the LEFT position of the checkbox
            cbx(i).Top = 10 + i * 30                            'set the TOP pos. based on which checkbox it is
            cbx(i).Text = CStr(dr.Item("SensorID")) & "  " & CStr(dr.Item("NameLong")) 'create the TEXT by combining the SensorID and NameLong fields with a space in between
            cbx(i).Width = Len(cbx(i).Text) * 7     'multiply num of chars by 7 to get proper width
' I multiplied by 7 because it was the smallest number that would allow a single character to fit based
'on the default font - for larger or BOLD fonts 7 would need to be increased
            cbx(i).Visible = True                   'make the control visible
            cbx(i).Tag = i                          'use Tag as a way of addressing the specific checkbox
'it appears from my testing so far that .Tag works like an INDEX in this case.
            Me.TabDigOut.Controls.Add(cbx(i))       'add this specific checkbox to the specific tab on the form
            AddHandler cbx(i).CheckedChanged, AddressOf cbx_CheckedChanged  'AddHandler
            i = i + 1  ' increment i by one for the next subscript
        Next
    End Sub

Finally, I needed to see how the system reacted to checking and un-checking the checkboxes so I tried a few things that just popped up message boxes. Most of these are commented out below but they do work. The stuff in the IF-ELSE-ENDIF is just there for my debugging purposes for now. I will add the actual code later as I continue my development. But as you can see, this does work.

Private Sub cbx_CheckedChanged(ByVal sender As System.Object, ByVal e As System.EventArgs)

        'MsgBox(sender.text)     '   This will provide the TEXT of the CheckBox
        'MsgBox(sender.name)     '   This will provide the NAME of the CheckBox
        'MsgBox(sender.tag)      '   This will provide the TAG (or 'INDEX') of the CheckBox)

        'Any code can go in the IF - ELSE - ENDIF statements below as required for your application
        If cbx(sender.tag).Checked Then
            MsgBox("Check Box " & sender.tag & "  " & "Named " & sender.name & " and an Index # of " & sender.tag & " has been CHECKED")
        Else
            MsgBox("Check Box " & sender.tag & "  " & "Named " & sender.name & " and an Index # of " & sender.tag & " has been UN-CHECKED")
        End If
    End Sub

I look forward to any comments or feedback regarding this as I know this has been troubling a lot of people who are migrating from VB6 and are looking to dynamically create control arrays.

Thanks for your time and all of your previous help.

Charlie
Avatar of Jeff Certain
Jeff Certain
Flag of United States of America image

Well, the first thing I'd do is leverage the System.Collections.Generic namespace to replace those nasty non-type-safe arrays. For example, replace Dim cbx() As CheckBox with Private cbx as New List(Of CheckBox). This gives you design-time type-checking to make sure you're only putting checkboxes in that "array". It also alleviates the need to redim, since you can simply Add/Remove the checkboxes.

I'd also consider adding a DataTable property that I set from outside the control. You'd be able to move all your database access code to a data access alyer than, and the reusability of the control would be improved, since you wouldn't be bound to a specific table in a specific database. (Or, for that matter, even a particular database type, since you can use data providers against multiple database engines to provide a datatable).
Avatar of Sancler
Sancler

Charlie

Your code looks fine.  But I've one question: why?

What you've produced is effectively a DataGrid or DataGridView with a CheckBox column and a readonly Name/Description column.  Because you don't want all the data from your datatable, some customisation of any grid would be necessary.  But I'm not sure that coding that would be any more taxing than creating your control array.  And a lot would be done for you automatically - e.g. the number of rows - and there would be the advantage (?) of databinding.

I appreciate that that may look like quibbling about the particular example you've used, rather than addressing the more basic question of whether control arrays are "a good thing".  But I do think that the answer to that basic question is more open than is sometimes assumed.  There's an awful lot of things that we were used to pre VB.NET and so which we're tempted to want to carry over.  But - with one exception, which your approach doesn't address - I'm not sure that there is anything that could be done with VB6 control arrays which cannot be done (probably as easily, once we get used to .NET ways) with VB.NET.  The exception is the design time facility of creating one control, giving it an index, and then copying it, each copy being added to the control array.

Let me stress that I am not suggesting that control arrays are "a bad thing".  But what I am suggesting is that the simple fact that we found them useful in VB6 may not, of itself, be a good enough reason for wanting to replicate them in VB.NET.  In each case, before constructing a control array in VB.NET, I would want to ask "what is it that this is going to allow me to do, or do more easily, that is not possible, or too difficult, using the facilities that are already present in VB.NET"?  And, though circumstances will alter cases and there is obviously a large element of personal choice in this, my answer would very often be "Nothing".

Roger
I just noticed that my suggestion re: generics is not applicable, since that's a VB2005 feature...
Avatar of charlieb01

ASKER

Roger,

I appreciate your comments. My main reason for getting into this coding in the first place was that my company uses a core program (very large) for every piece of test equipment it sells. We are required to provide the source code and a copy of the development language (VB) to our clients. VB6 is becomming very difficult to purchase.
I have a software engineer who told me that she could not develop the core program we now have in VB6 into VB.NET - So this problem has been around for several years. Since it is a small company and we cannot afford to hire an outside contractor or another software engineer, I decided to dig out my old programming tools (that I have not used in many years) and attack the problem.
My first goal is to replicate the old VB6 core program as closely as possible so my software engineer will be able to easily follow it.
My next goal is to use what I am learning about VB.NET and apply that knowledge to developing a NEW core program with a fresher feel and look and something that will be easier to customize for each client.

But since you brought it up, I would be curious about how to develop a datagrid that is programmatically bound to a database table that could hold checkboxes and radio (or Option) buttons. My software engineer has had me purchase ComponentOne ActiveX products before for every project and I was told one of the reasons was that only TrueDBGrid could handle radio buttons or checkboxes. I would rather not spend the extra money if it is not necessary.

I appreciate any feedback.

Thanks,
Charlie
ASKER CERTIFIED SOLUTION
Avatar of Sancler
Sancler

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial