The VB6 Key property and using it to reference collections in VB.NET

I am trying to migrate from VB6 to VB.NET and I simply cannot get my head around the removal of the Key property in collections.

The latest headache this is causing me is a ListView, columns created at runtime which can potentially differ.To populate that ListView with data, I want to be able to say:

Me.ListView1.Items(1).SubItems("PhoneNumber").Text = "Example"

In VB6 it wasn't exactly obvious how to do this but you could use the expression:

Me.ListView1.ListItems(1).SubItems(Me.ListView1.ColumnHeaders("PhoneNumber").SubItemIndex).Text = "Example"

In VB.NET I see no way whatsoever to reference a ListView SubItem by anything other than a numeric index, and one which I have no control over at that. This same issue has caused me problems with TreeView Nodes, ImageList Images, Toolbar Buttons and several other controls which implement collections.

I know that technically I can use something called a HastTable to circumvent this inadequacy but I find HashTables more than a little inconvenient for my purposes. I am assuming that there must be people out there who used to write lines of code such as Me.ListView1.ListItems(1).SmallIcon = "OpenFolder" and have already had to change their way of thinking to accomodate the complete lack of text referencing for collections in VB.NET. What I really more than a way to work around this latest inconvenience is some guidence as to what I should be doing in order that I don't feel the need for the Key property, maybe in the form of a book recommendation, a URL about VB6 migration which addresses this issue or anything else which might leave me less puzzled.

Many Thanks
Who is Participating?

[Product update] Infrastructure Analysis Tool is now available with Business Accounts.Learn More

I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

This code will do it.  The Button1_Click procedure has the effect of setting some text in the listview.  The column that is changed depends on whichever column has the string "name goes here" as its caption.  This is the same text that would be displayed if the listview has visible column headers.

The FindColumn() function takes two parameters which are the listview itself and the caption text which is being looked for.  It returns the index of the relevant column as an integer.  You need this function if you are going to use this technique.  Once the function is written (and I have done it for you) it is very easy to use, as shown in the Button1_Click() event procedure.

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        ListView1.Items(1).SubItems(FindColumn(ListView1, "name goes here")).Text = "set this text"
    End Sub

    Private Function FindColumn(ByVal TheListView As ListView, ByVal ColumnName As String) As Integer
        Dim Count As Integer

        For Count = 0 To TheListView.Columns.Count - 1
            If ListView1.Columns(Count).Text = ColumnName Then
                Return Count
            End If
        Throw New Exception("The column name was not in the collection")
    End Function

Hope this helps.

Ads B


It is perhaps worth mentioning that the HashTable event would execute faster in some circumstances than the method I have given.  But my method is simpler and tyhat seems to me to be important to you.

Nicer than both of these techniques would be if you wrote your own class which inherited from the listview and exposed a Collection which had a key property and returned either the index of the column.
I know how you feel i hate it also :

well my suggestion is the enum

Public Enum ListviewTypes
  NameOfField  = 3 'IndexNumber

End Enum

so you do  Listview.items(nameoffield) whatever

also if you wanna store extra data (key or tag) just make an invincible column.
wwarbyAuthor Commented:
Thanks mate, but the trouble with Enums is that they're declared at design time and there's no scope to change them on the fly which in this case, I want to do. Besides that, my issue here is that I don't want to keep using work-arounds to get over this problem by storing strings in the form of Constants, Variables, Enums, Hashtables etc. I'm positive that Microsoft didn't take out this functionality just to annoy me, they must have genuinely believed it was no longer required and I want to know what made them reach that conclusion.
Become a Microsoft Certified Solutions Expert

This course teaches how to install and configure Windows Server 2012 R2.  It is the first step on your path to becoming a Microsoft Certified Solutions Expert (MCSE).

wwarbyAuthor Commented:

Thanks a lot for your answer, a function such as the one you suggest here is something I had considered doing also. But there's no denying that this is still a workaround. How do you personally get around this problem? I am very much leaning towards writing a class that wraps each control (TreeView, ListView, Toolbar, ImageList etc.) and giving them all a Key property, but I just don't feel like I should have to do that. If I need it so badly that I have to build improved controls, why doesn't everybody else? What does the average developer do to get round this problem, or does everyone just accept the HashTable answer?

Apologies if I appear an awkward customer here, the trouble is, I'm a competent VB6 programmer and I have already considered most work-arounds to circumvent this inconvenience, I just believe I must actually be doing things wrong if I need to work-around this problem at all.
>How do you personally get around this problem?

well, that's a bit hypothetical because I have never been in a situation where the user could choose at runtime what captions a listview's columns would have.  It would be interesting to know some more about your application and why this scenario is required.

I think if I were in this situation then I would probably choose between the three methods I have given under the following circumstances:-

1.  FindColumn() utility function if it is throw-away code or if the number of calls to the function are not expected to affect performance, or if performance doesn't matter.

2.  HashTable method if there are only one or two listviews in the program which need it... I wouldn't want to litter my code with lots of these messy hashtables.  This method is also faster than method 1.

3.  The inherited control option if there are lots if listviews to do (possibly in more than one program) and performance is an issue.

I also think that the three methods are in order of increasing programmer effort required.  I always choose the lowest effort if there are no other overriding factors.

Sorry for the academic answer, but to me it is an academic question :-)

wwarbyAuthor Commented:

Sorry to have to keep pressing on this one. The thing is, it isn't just ListView columns I have a problem with. Take for example Toolbar Buttons. The application I am designing populates Toolbar with buttons at runtime and adds Icons to each button from an ImageList. To add the Images from an ImageList. I have already had to use a HashTable to give each of the Icons a name which I am not happy about.

When the user clicks a button, I want to know what button was clicked so as to determine what action to take. This is the code I'm stuck with unless I use HashTables...

Private Sub tbrMain_ButtonClick(ByVal sender As Object, ByVal e As System.Windows.Forms.ToolBarButtonClickEventArgs) Handles tbrMain.ButtonClick
        Select Case Me.tbrMain.Buttons.IndexOf(e.Button)
            Case 0
                'Some Code
            Case 1
                'Some Code
            Case 2
                'Some Code
        End Select
End Sub

Now, if at some later stage I decide to add a button after Button 0, I would have to revisit that piece of code and change all the indexes to reflect the change. Now suppose I have a line of code in a hundred places that says:

If Me.tbrMain.Buttons(2).Pressed Then....

Every one of those lines would also need changing as soon as I changed the number of buttons in that toolbar. I want to say Me.tbrMain.Buttons("Save").Pressed, because then it doesn't matter what order they are in. I can certainly say Me.tbrMain.Buttons(HashTable1("Save")).Pressed but I find HashTables incredibly inconvenient to maintain.

I apologise if I'm not making myself clear, the trouble is that the ListView columns is just one example of a near infinite number of times I want to ask a collection to return a specific object, and provide a string for the collection to evaluate. The reason I find Integer referencing so infuriating is that I have no control over it. If you change the items in a collection, the Indexes all change dynamically, which makes hard-coding the Indexes inappropriate whenever you want to change the collection's members at runtime, and the problem is the same whether adding buttons to a toolbar, nodes to a treeview, columns to a listview, icons to an imagelist or fields to an ADO dataset or anything else. As far as I can see, numeric indexing is simply impractical when dealing with collections that change on the fly, and the fact that I find this problem places such severe restrictions on me suggests to me that my way of thinking about collections is fundamentally wrong.

I'll gladly increase the points on this question if you can tell me what I'm doing wrong, but I simply do not want to side-step the problem with auxiliary functions, HashTables, custom controls or Enumerations. Microsoft surely would not have taken out the functionality I depended on only to make people find their own ways to put it back in.
> I find HashTables incredibly inconvenient to maintain
Surely there is some element of exaggeration here.

> the ListView columns is just one example of a near infinite number of times I want to ask a collection to return a specific object, and provide a string for the collection to evaluate
How big does a number have to be before it is "near infinite"?

Sorry m8, just couldn't resist the comedy hair splitting. ;-)

Anyway, it would really help if you could providean example of why you need to do any of this.  Why can you not set all this up at design time.  What is it that you are doing that causes all the indices to change "on the fly" and why do you have to do it.  The example yo give in your "select... case" statement would be adequately dealt with by the enumerations solution that Mikal613  gave you.

As I explained earlier, it is hard for me to help you without this explanation because for me this just isn't a problem.



Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
wwarbyAuthor Commented:
Ok, here are a few examples of instances where I would want to change these collections at runtime. All of these are things I have done previously in VB6...

Populating an ImageList with icons associated with particular file extensions in Windows. Without knowing what files are going to be placed in a ListView I cannot know what icons are going to need to be in the ImageList.

Populating a ListView with columns that match the columns in a database Table, the table being selected at runtime.

Showing or not showing certain Toolbar buttons, ListView columns and TreeView nodes according to user permissions retreived from a database at runtime.

Populating a TreeView with nodes from an XML file.

Showing or not showing Tabs in a TabControl according to which Windows are currently open in an MDI form.

Populating an ImageList from code using Embedded Resource Icon Files since that seems to be the only way to get a Windows XP (alpha-blended) icon into an ImageList.

Those are a few examples of things I have done in the past at runtime, but even at design time, even if I used Enumerations for my Select...Case example, it would mean every time I made a change to that collection I would have to go through and resequence that Enumeration. Now, that might be acceptable if I had 10 toolbar buttons but suppose we're talking about an ImageList with 200 icons in it.

Perhaps I am exaggerating the inconvenience this is causing me or the number of times I encounter the problem. I'm writing my first app in VB.NET, it's for maintaining a database of movies and so far I have felt the need for the Key property about 5 times, this in two days of development.

The problem I'm on now is that I'm going fill a ListView when the user clicks on a node in a TreeView. I need to populate about 12 columns and I don't want to use Me.lvwFilms.SubItems(1).Text = "DVD" because I know I am going to end up changing the columns before I've finished development. Further to this, there's a context menu associated with the ListView that will allow certain properties of the selected movies to be changed, for example if I select a movie where the DVD column is set to True, I want the context menu item mnuDVD to be checked. To do this, I need to say something like

If Me.lvwFilms.SelectedItems(1).SubItems(1).Text = "True" Then Me.mnuDVD.Checked = True

Further to this, if I double click a film in the ListView, I want to populate an editor form so I'm going to have loads of statements like frmEditor.txtGenre.Text = Me.lvwFilms.SelectedItems(1).SubItems(4).Text

If I want to move the columns round later, not only do I have to be aware of the implications when populating the ListView but also any instance where I may have referred to that ListView. Surely you can see with that in mind that  the larger the application grows, the more of a problem this will be - I simply will not be able to remember all the places where I have hard-coded indexes.

HashTables are a way around this, and in the absence of preferable alternatives they are what I will use. But they still have to be maintained. If I change the columns in my ListView then so I must also change my HashTable.

The fact that you don't seem to find this a problem suggests to me that you either didn't use VB6 or you did, but you never used the Key property. What I really wanted was to speak to someone who started out feeling the same as I do and found a way to satisfy themselves. That said, you seem to know the architecture fairly well and if there was an answer out there that I wanted to hear, I'm sure you would know it. I really believed that Microsoft just would not have taken out the Key property and left me with such a poor alternative as a HashTable but perhaps that really is the case. I'll wait to see your response to this post but whether I like what I hear or not, I'll hand over the points because you've certainly gone the extra mile to answer my question.

Thanks mate.
wwarbyAuthor Commented:
Well, I didn't get the answer I was looking for and to be honest, I'm still as frustrated as I was before on this issue. That said, AdsB, you made the effort to help and it's only fair to hand over the points since no-one else had much to say on the subject. Thanks for the effort.

It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
.NET Programming

From novice to tech pro — start learning today.