VBA Dictionary - Adding an item overwrites all items - vba

I have a Problem with a Dictionary. If I add an Object to the Dictionary, it overwrites the whole containing Items with the added item.
After adding all elements, the Dictionary contains the right number of items, but the items are all the last added item.
For Each shp In pg.Shapes
Dim tmp As New cls_dtyp_link
//Filling tmp with Variables - not Displayed - tmp.link_obj is the Key
If link_dic.Exists(tmp.link_obj) Then
Debug.Print "not added:" & tmp.link_obj
Else
link_dic.Add tmp.link_obj, tmp
End If
Next
The cls_dtyp_link Class:
Public link_ne As String
Public link_obj As String
Public link_ref As Visio.Shape
Public obj_left As String
Public obj_right As String
Public ref_left As Visio.Shape
Public ref_right As Visio.Shape
Public basekey_left As String
Public basekey_right As String
Public root_site_ne_left As String
Public root_site_ne_right As String
Public root_obj_left As String
Public root_obj_right As String
Public ref_root_left As Visio.Shape
Public ref_root_right As Visio.Shape
Public hops As Integer
Public geht_zu_konzentrator As Boolean
Thank you for your help!

The problem comes from the Dim tmp As New cls_dtyp_link statement: what is does is adding a reference to the existing cls_dtyp_link object, it does not instantiate a new one as you would expect. If you want to create a new object, kill the existing one explicitly at the end of your loop: Set cls_dtyp_link = Nothing. Then a new instance will be created every time you re-enter the loop.
Generally, declaring and instantiating in one go is considered bad practice in VBA, because of issues like the one you encountered. I would suggest Dim-ing the object outside the loop, and Set-ting it inside the loop.
Also, have a look here:
VBA: Difference in two ways of declaring a new object? (Trying to understand why my solution works)
I hope that helps!

Related

SortedSet in Class does not expose correctly

I have a weird problem that I can't seem to figure out. Even weirder is that I'm fairly sure it has worked in the past, but not anymore.
I have a class where I define a variable as SortedSet. In a function, I can reference the variable, but its SortedSet attributes are not exposed. If I use them anyway, some of them work, others don't. If I create that variable inside my function, all works as expected.
This is the code:
Public Class MyTest
Public MySortedSet = New SortedSet(Of String)()
Public Sub New()
Dim MySortedSet2 = New SortedSet(Of String)()
'Constructor. To use this, add Dim MyTest As MyTest to the Form1_load sub.
Me.MySortedSet.add("Test")
For Each Item In Me.MySortedSet
MsgBox(Item) 'This does print Test
Next Item
Me.MySortedSet.add '.add not exposed
MySortedSet2.add '.add is exposed
End Sub
End Class
See the screenshot below. The first example only has 4 items, where the 2nd example has a full list of parameters. I need to fix this using the first example, so the ElementAt one works. It works in the second example, but not in the first. It gives the error that ElementAt is not part of this object.
How can I get the full list of parameters for me.MySortedSet.??????
You should declare MySortedSet as a Property:
Public Class MyTest
Public Property MySortedSet As New SortedSet(Of String)()
Public Sub New()
Dim MySortedSet2 As New SortedSet(Of String)()
Me.MySortedSet.Add("Test")
For Each item As String In Me.MySortedSet
Debug.WriteLine(item)
Next item
Me.MySortedSet.Add("Test")
MySortedSet2.Add("Test")
End Sub
End Class
You should also indicate variable types every time you declare a variable, even in a For Each statement.

Infinite amount of Collection VBA

Here is the screen of my problem which is infinite amount of collections.
I want the collection be added to object property just once. Not like this:
http://postimg.org/image/o6da95j0f/
(screen showing the problem with "watch" of collection in VBA
Public Sub testCollections()
Dim index As Long
index = 1
Dim OJsonElement As JsonElement
Dim newColl As New Collection
Dim str As String
Call addColl(OJsonElement, newColl)
For Each OJsonElement In newColl
Debug.Print "THE NAME IS:" & OJsonElement.name
Next OJsonElement
End Sub
Function addColl(obj1 As JsonElement, nextCollection As Collection)
Dim i As Long
Set nextCollection = New Collection
Set obj1 = New JsonElement
Set obj1.valueCollection = nextCollection
obj1.name = "CityName"
obj1.value = "type"
nextCollection.Add obj1
'obj1.ValueType = nextCollection
'nextCollection.Add nextCollection
End Function
Class:
Public name As String
Public nameCollection As Collection
Public value As Variant
Public ValueType As String
Public valueCollection As Collection
I don't really understand well your code, but I will limit to explain you why it happens what you see in your watcher. The line:
Set obj1.valueCollection = nextCollection
is adding the new collection into the obj1 property valueCollection. Then, two lines after, you say:
nextCollection.Add obj1
which means you're adding the obj1 into its own property, so creating an infinite nesting. I'd like to help you but for that I'd need to understand what you want to reach with your code. But sticking to your request I want the collection be added to object property just once, I would just suggest you to remove the line nextCollection.Add obj1, which (at least from the perspective of who doesn't know the project purpose) does not seem to do anything useful but an infinite nesting.

VB.NET Add row data to public class / public field of type List(Of T) Object reference not set to an instance of an object

I am trying to add rows to a public class that has public fields and am getting an error: Object reference not set to an instance of an object
Public Class EmailRecipient
EmailAddress As String = ""
FullName As String = ""
End Class
Public Class EmailDetails
Public FromEmail As String = ""
Public ToEmails As List(Of Emails) = nothing
End Class
Public Sub SetEmailDetails
'Populate EmailRecipient Class
Dim er As New EmailRecipient
er.EmailAddress = "rodney#norespect.com"
er.FullName = "Rodney Dangerfield"
'Populate EmailDetails Class
Dim ed As New EmailDetails
ed.FromEmail = "sender#danger.com" 'This works fine
ed.ToEmails.Add(er) 'Here error happens
End Sub
I'm guessing I need to create an instance of the EmailRecipient class before I can add an item to it.
Not sure how to do that with a Public Field in a Public Class??
It's been a rough day. I got up this morning, put a shirt on and a button fell off. I picked up my briefcase and the handle came off. I'm afraid to go to the bathroom.
Thanks for the help :-)
I'm guessing I need to create an instance of the EmailRecipient class
You already have an instance of the EmailRecipient class. That's your er variable. You actually have two errors here. First, you explicitly set ToEmails to Nothing:
Public ToEmails As List(Of Emails) = nothing
This means that your ToEmails variable is a Null Reference. It doesn't have an actual object yet.
The second issue is that you shouldn't get that excpetion, because this shouldn't even compile. You define ToEmails as a List(Of Emails), but tried to add an object of type "EmailRecepient" to it. That should be a compiler error. If it's not, you need to turn Option Strict or Option Infer back on.
So what you really need is an instance of a the List(Of EmailRecipient) type. Fix the bad line of code like this:
Public ToEmails As New List(Of EmailRecipient)

vb.net 2003 Arraylist of structure which have an arraylist

I have two structures
Public Structure myResearchData
Public batchName As String
Public arraylistRData As ArrayList
End Structure
Public Structure myResearchSubData
Public researchDescription As String
Public recordingDate As Date
Public book As String
Public page As String
Public year As String
Public fee As String
End Structure
I initialize them in a sub
Dim MyResearchDataAList As New ArrayList
Dim MyResearchData As myResearchData
MyResearchData.arraylistRData = New ArrayList
Dim MyResearchSubData As myResearchSubData
I have an arraylist of myResearchSubData which is MyResearchData.arraylistRData and added it inside MyResearchDataAList. But when I cleared MyResearchData.arraylistRData the arraylist inside MyResearchDataAList is also cleared. I thought that once it is added to MyResearchDataAList it will hold the arraylist but it is also clered. Below is the process that I have done.
MyResearchSubData.recordingDate = Date.Parse(Date)
MyResearchSubData.book = Book
MyResearchSubData.page = Page
MyResearchSubData.year = Year
MyResearchSubData.fee = Fee
Put data in the structure of MyResearchSubData
MyResearchData.arraylistRData.Add(MyResearchSubData)
Added it in MyResearchData.arraylistRData
MyResearchDataAList.Add(MyResearchData)
Added it in MyResearchDataAList
MyResearchData.arraylistRData.Clear()
Cleared MyResearchData.arraylistRData for new data to be put in but it also clears the arraylist inside MyResearchDataAList and didn't old the contents of the arraylist
Thanks in advance for those who can help me with this problem
That is happening because it is actually the same items being added to the array list, as they are added by reference. You want to actually add a COPY of each item to your arrayList.
I generally find the easiest way to do something like that is to add a clone of the object during a loop.
To do that you may need to implement ICloneable to get the proper copy. At the level of the myResearchSubData however, it appears that you can just use the memberwiseClone Method.
If you change the to a class instead of a structure you can use the clone like this:
Public Class myResearchSubData
Implements ICloneable
Public researchDescription As String
Public recordingDate As Date
Public book As String
Public page As String
Public year As String
Public fee As String
Public Function Clone() As Object Implements System.ICloneable.Clone
Return Me.MemberwiseClone
End Function
End Class
Then you would want to loop through your original list of myResearchSubData and add its clone to the second list. Something like:
For Each item as myResearchSubData in MyResearchData.arraylistRData
MyResearchDataAList.Add(CType(item.Clone, myResearchSubData))
Next
If you want to continue to use the structures then I would use the same loop type and make a function that creates a new myResearchSubData and copies the data from the original over to the new one.
For Each item as myResearchSubData in MyResearchData.arraylistRData
MyResearchDataAList.Add(CloneStructure(item))
Next

Unable to cast custom listviewitem class in vb.net

This is a followup question to another question I asked earlier. I thought I had everything I needed, but I'm running into another issue. I'm trying to use a custom listviewitem class that attaches additional information to a lisview item. Here is the class:
Public Class albumListViewItem
Inherits ListViewItem
Public hash As String
Public id As Integer
Public provider As String
Public providerID As String
Public providerURL As String
Public providerArtistID As String
Public albumName As String
Public albumType As String
Public numTracks As Integer
Public imageURLs() As String
Public genres() As String
Public styles() As String
Public label As String
Public year As String
Public country As String
Public rating As String
Public editorsPick As Boolean
Public sampleStreamURL As String
Public providerReview As String
End Class
When I try to cast a listviewitem to my custom class like this:
Dim albumItem As albumListViewItem = CType(lsvHidden.items.item(0), albumListViewItem)
I get the following error, "Unable to cast object of type 'System.Windows.Forms.ListViewItem' to type 'AudioMatic.albumListViewItem'."
What am I missing here?
From your previous question and this one, I think a better fit for your problem would be to use a regular ListViewItem and store the accessory information in ListViewItem.Tag
You can do
Dim listViewItem As New ListViewItem("SomeText")
Dim albumInfo As New albumListViewItem()
albumInfo.albumName = "SomeAlbum"
...
listViewItem.Tag = albumInfo
listView1.Items.Add(listViewItem)
and then retrieve it like this
Dim selectedItem As ListViewItem = listView1.SelectedItems(0).Item
Dim alubmInfo As albumListViewItem = TryCast(selectedItem.Tag, alubmListViewItem)
Dim albumName as String = albumInfo.albumName
see if this solution will work for you.
If you step through the code and watch the variable "lsvHidden.items.item(0)" you should be able to first tell if it is in fact of type "albumListViewItem" or something else. Are you sure it was albumListViewItem that was added to the list in the first place?
Some alternatives to what you are doing;
1. You can implement an object and store it in the tag of the ListViewItem.
2. The following article seems to describe another approach of adding Columns to the listview to allow storing extra information on the listview itself; http://www.codeproject.com/KB/list/ListViewExtendedItem.aspx
I can appreciate your situation as I would have expected that to work. And I can see advantages and reasons for doing it that way as well. Not sure if the code project sample is adaptable to what you need, so you'll need to review the concept.
The working code:
Public Class albumListViewItem
Inherits ListViewItem
Public hash As String
Public id As Integer
Public provider As String
Public providerID As String
Public providerURL As String
Public providerArtistID As String
Public albumName As String
Public albumType As String
Public numTracks As Integer
Public imageURLs() As String
Public genres() As String
Public styles() As String
Public label As String
Public year As String
Public country As String
Public rating As String
Public editorsPick As Boolean
Public sampleStreamURL As String
Public providerReview As String
End Class
Storing information using listviewitem.tag:
Dim listViewItem As New ListViewItem("SomeText")
Dim albumItem As New albumListViewItem
albumItem.albumName = "Test Album"
albumItem.id = "testid"
albumItem.Text = albumItem.albumName
albumItem.year = "2011"
albumItem.numTracks = 10
'....
listViewItem.Tag = albumItem
'add viewable items to listview
albumItem.SubItems.Add(albumItem.year)
albumItem.SubItems.Add(albumItem.numTracks)
'....
ListView1.Items.Add(albumItem)
Reading the information that was previously stored:
Dim albumInfo As albumListViewItem = CType(ListView1.SelectedItems(0), albumListViewItem)
Dim id as string = alumInfo.id