wwarby
asked on
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).SubI tems("Phon eNumber"). Text = "Example"
In VB6 it wasn't exactly obvious how to do this but you could use the expression:
Me.ListView1.ListItems(1). SubItems(M e.ListView 1.ColumnHe aders("Pho neNumber") .SubItemIn dex).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
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).SubI
In VB6 it wasn't exactly obvious how to do this but you could use the expression:
Me.ListView1.ListItems(1).
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).
Many Thanks
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.
well my suggestion is the enum
Public Enum ListviewTypes
NameOfField = 3 'IndexNumber
End Enum
so you do Listview.items(nameoffield
also if you wanna store extra data (key or tag) just make an invincible column.
ASKER
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.
ASKER
AdsB,
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.
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 :-)
AdsB
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 :-)
AdsB
ASKER
AdsB,
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.ToolB arButtonCl ickEventAr gs) 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).Pres sed 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(HashTab le1("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.
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.ToolB
Select Case Me.tbrMain.Buttons.IndexOf
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).Pres
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")
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.
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
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).Te xt = "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).SubItem s(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).SubItem s(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.
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).Te
If Me.lvwFilms.SelectedItems(
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(
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.
ASKER
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.
-William
-William
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).SubItem
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).T
Return Count
End If
Next
Throw New Exception("The column name was not in the collection")
End Function
Hope this helps.
Ads B
PS.
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.