I want to add a new PropertyGrid control every time an item is added to a ListBox.
I have a ListBox, with an "Add Item" button underneath (and also a "Remove Item" button). Every time an item is added to the list, I want to create a new PropertyGrid corresponding to each item. The relevent PropertyGrid should appear when an item is selected in the ListBox, and the user can edit the properties of that item.

The only way I can think of doing this is by creating a dynamic array which has length ListBox1.items.count, but I'm not sure it's possible to create an array of controls.

Any ideas people?

It is certainily possible to create an array of controls. There are several ways to keep track of them. You could use a dynamic array or a dictionary (to name two). Just declare the array as follows

Dim ctrlarray(ListView1.Items.Count - 1) As PropertyGrid

You create a new instance as

ctrlarray(i) = New PropertyGrid

If you need to handle events for it you will have to use AddHandler to connect the Sub to that instance. The first Dim assumes that the number of items in the listview does not change (or at least does not increase). If the number rof items is dynamic you will have to declare the initial size as () and use ReDim as items are added. Another option is to use a dictionary as in

Dim ctrlarray as New Dictionary(Of Integer, PpropertyGrid)

And you reference properties and methods of the array items as

ctrlarray(i).etc

Many thanks Reverend Jim! My problem is by no means solved, but you've helped alot.

I may be back with another question...

Right, I'm still a VB newbie, so haven't used AddHandler. But I came up with something, and now I have a problem.

I basically added a panel (Panel1) to my form with no controls in it at first. I then modified my click command for the "Add Items" button so that it adds a PropertyGrid to the panel every time it is clicked.

Now, my problem is that I want to bring the associated PropertyGrid to the front of the form when I change the selected index of my listBox (i.e. when I select a different item in the list, the associated propertyGrid should appear).

Here's a snippet;

Private Sub cmdAddUEL_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles cmdAddUEL.Click
        Static count As Integer
        Static name As String
        count = listBox1.Items.Count
        count = count + 1
        name = "Element " & count
        listBox1.Items.Add(name)
        listBox1.SelectedIndex = count - 1

        Dim ctrlArray() As PropertyGrid
        ReDim ctrlArray(count - 1)
        ctrlArray(count - 1) = New PropertyGrid
        ctrlArray(count - 1).SelectedObject = New SimpleProperties()
        ctrlArray(count - 1).Width = 272
        ctrlArray(count - 1).Height = 188
        Panel1.Controls.Add(ctrlArray(count - 1))
    End Sub

This handles the click event, and essentially adds an item to the listBox1 and a propertyGrid to the dynamic array ctrlArray().

Private Sub lstUELs_SelectedIndexChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles lstUELs.SelectedIndexChanged
        Dim currentItem As Object
        Dim i As Integer

        If listBox1.SelectedIndex <> -1 Then
            currentItem = listBox1.SelectedItem.ToString()
            i = listBox1.Items.IndexOf(currentItem)
            Panel1.Controls.Item(i).BringToFront()
        End If
    End Sub

What this should do is bring the matching control to the front, but I get an error on the first click saying that index 0 is out of range in the last line of the if statement.

Where am I going wrong?

In line 8 you have

listBox1.SelectedIndex = count - 1

But you execute this before you have added the new control to the control array. Changing the SelectedIndex triggers the event before you have added the control so when you try to access it in the array it is not there yet. Try creating the control and adding it to the array before you add the listbox entry and change the selected index.

--- first sip of morning coffee ---

On rereading I see it's complaining about index 0. Before I have a deeper look I have a question. Isn't it the new panel you want to bring to the front in which case the statement should be

ctrlArray(i).BringToFront()

instead of

Panel1.Controls.Item(i).BringToFront()

I can't bring ctrlArray(i) to the front because it's defined in the button click event, or at least I don't know how to!

I'm adding a new control to panel1 which I've put on the form every time I click the button, so what I'm doing should work, and does!

What I did was get rid of line 8 (for the moment!) and rather than having the BringToFront command, I've got the following lines in the selected index change sub;

Private Sub listBox1_SelectedIndexChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles listBox1.SelectedIndexChanged
        Dim i As Integer
        Dim j As Integer
        i = listBox1.SelectedIndex

        If i <> -1 Then
            Panel1.Controls.Item(i).Visible = True

            If i <> 0 Then
                For j = 0 To i - 1
                    Panel1.Controls.Item(j).Visible = False
                Next
            End If

            If i <> listBox1.Items.Count Then
                For j = i + 1 To lstUELs.Items.Count - 1
                    Panel1.Controls.Item(j).Visible = False
                Next
            End If
        End If    
    End Sub

So, say, if the 3rd item is selected in listBox1, the 3rd control in Panel1 will become visible, and all other controls will become invisible.

Declare ctrlarray at the class level as

Public Class form1
 
    Public ctrlArray() As PropertyGrid

Does your code work now?

If I declare ctrlArray at class level, I get the following error when I use ctrlArray in any of the subs;

"Object reference not set to an instance of an object."

Referring to ctrlArray(i). Specifically, the error first occurs in line 11 of the most recent code, where I've commented out the Panel1 dependence and replaced it with

ctrlArray(j).Visible = False

.
But using the panel to store the controls seems to be working fine for me. However I anticipate a problem in the future when I need to make reference to and store values input in each PropertyGrid.

If you are getting that error it is because you haven't assigned a PropertyGrid instance to ctrlArray(j). When you get that error in the IDE, check the calue of ctrlArray(j) and also (j-1) and see if you are "off-by-one". If all of ctrlArray is unassigned then check where you do the assignment to see if you are doing it properly. If you haven't incremented the index properly then perhaps all your assignments are going to the same index.

Sorry Rev, I don't quite follow. It's definitely increasing the size of ctrlArray by 1 every time I click the "Add Item" button, but the control just wont show and I get the same error. How do I assign a propertyGrid instance in the SelectedIndexChanged sub?
What I have is the following;

Public Class form1    
    Public ctrlArray() As PropertyGrid

    Private Sub cmdAddUEL_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles cmdAddUEL.Click
        Static count As Integer
        Static name As String
        count = listBox1.Items.Count
        count = count + 1
        name = "Element " & count
        listBox1.Items.Add(name)

        ReDim ctrlArray(count - 1)
        ctrlArray(count - 1) = New PropertyGrid
        ctrlArray(count - 1).SelectedObject = New SimpleProperties()
        ctrlArray(count - 1).Width = 272
        ctrlArray(count - 1).Height = 188

        listBox1.SelectedIndex = count - 1
    End Sub

    Private Sub listBox1_SelectedIndexChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles listbox1.SelectedIndexChanged
        Dim i As Integer
        Dim j As Integer
        Dim count As Integer
        i = listBox1.SelectedIndex
        count = listBox1.Items.Count

        If i <> -1 Then
            ctrlArray(i).Visible = True

            If i <> 0 And i <> count Then
                For j = 0 To i - 1
                    ctrlArray(j).Visible = False
                Next
                For j = i + 1 To count - 1
                    ctrlArray(j).Visible = False
                Next
            End If
        End If
    End Sub

I'm unsure as to how I check if I've even assigned ctrlArray correctly. Really appreciate your help

Is it possible to zip the entire project and attach it to your post so I can see the whole thing and look at it in the IDE on my PC?

Try this code and see how it compares to yours

Public Class Form1

    Public ctrlArray As New Dictionary(Of Integer, PropertyGrid)
    Public currgrid As PropertyGrid

    Private Sub btnAddItem_Click(sender As System.Object, e As System.EventArgs) Handles btnAddItem.Click

        'create new listbox item and associated property grid

        Dim newitem As String = "item " & ctrlArray.Count
        Dim newprop As New PropertyGrid

        newprop.Location = New System.Drawing.Point(140, 13)
        newprop.Size = New System.Drawing.Size(300, 260)
        newprop.Visible = False

        'add the new property grid to the control array, then add new listitem.

        Me.Controls.Add(newprop)
        ctrlArray.Add(ctrlArray.Count, newprop)
        ListBox1.Items.Add(newitem)

    End Sub

    Private Sub ListBox1_SelectedIndexChanged(sender As System.Object, e As System.EventArgs) Handles ListBox1.SelectedIndexChanged

        'hide the current visible grid

        If Not currgrid Is Nothing Then
            currgrid.Visible = False
        End If

        'make the newly selected grid visible

        currgrid = ctrlArray(ListBox1.SelectedIndex)
        currgrid.Visible = True

    End Sub

End Class

Just start with a button and listbox and nothing else.

Cheers Rev. That works a treat! I'm now having trouble with removing items. I have a "Remove Item" button, and here's my sub;

Private Sub btnRemoveItem_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnRemoveItem.Click
        Dim currentIndex As Integer
        currentIndex = ListBox1.SelectedIndex

        'makes sure an item in the list is selected
        If currentIndex <> -1 Then
            ctrlArray.Remove(currentIndex)
            ListBox1.Items.RemoveAt(currentIndex)
            ListBox1.SelectedIndex = ListBox1.Items.Count - 1
        End If
    End Sub

and I get the error "The given key was not present in the dictionary.", referring to line 35 in your code.

I should have thought of this earlier. If the listbox items are unique then we can just use the listbox item as the dictionary key. Let me do a quick rewrrite and get back to you.

Got it (I think). Note that the variable names are different than the last example to reflect the more generic nature of the code.

Public Class Form1

    Public ctrlArray As New Dictionary(Of String, Label)
    Public curritem As Label

    Private Sub btnAddItem_Click(sender As System.Object, e As System.EventArgs) Handles btnAddItem.Click

        'create new listbox item and associated control

        Dim key As String = "item " & ctrlArray.Count
        Dim newitem As New Label

        newitem.Location = New System.Drawing.Point(140, 13)
        newitem.Size = New System.Drawing.Size(300, 260)
        newitem.Visible = False
        newitem.Text = key

        'add the new control to the control array, then add new listitem.

        Me.Controls.Add(newitem)
        ctrlArray.Add(key, newitem)
        ListBox1.Items.Add(key)

    End Sub

    Private Sub ListBox1_SelectedIndexChanged(sender As System.Object, e As System.EventArgs) Handles ListBox1.SelectedIndexChanged

        'hide the current visible control

        If Not curritem Is Nothing Then
            curritem.Visible = False
        End If

        'make the newly selected control visible

        If ListBox1.SelectedIndex >= 0 Then
            curritem = ctrlArray(ListBox1.SelectedItem)
            curritem.Visible = True
        End If

    End Sub

    Private Sub btnRemove_Click(sender As System.Object, e As System.EventArgs) Handles btnRemove.Click

        Dim key As String = ListBox1.SelectedItem
        curritem.Visible = False
        curritem = Nothing
        ctrlArray.Remove(key)
        ListBox1.Items.RemoveAt(ListBox1.SelectedIndex)

    End Sub

End Class

This one uses dynamic labels for testing. Replace "Label" with "PropertyGrid" or any other control. Don't forget to mark as solved if this is what you are looking for.

Good morning Rev,

Yes, that works nicely! I have a minor problem (which I anticipated) when an item in the middle of the list is removed and another added, since the list item added is not unique. But it's a very minor bug which I can work around. Many thanks for your help, you're a life saver!

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.