Erroneous Duplicate Root Nodes displayed in System.Windows.Forms.TreeView - vb.net

I'm hoping someone will be able to shed some light on this.
I'm encountering an issue in the System.Windows.Forms.TreeView control that is manifesting when I build an instance of a System.Windows.Forms.TreeView in MS Video Studio 2017.
Basically - The first time it is built, everything looks as expected. However, when I rebuild it at a later time (e.g.. after an update), the root node display gets duplicated. Multiple updates result in the TreeView displaying increasing multiple root notes.
The nodes are cleared each time before the TreeView gets built and rebuilt ( e.g., MyTreeView.Nodes.Clear() ).
I was able to verify that the root nodes do not physically get duplicated. The number of nodes in the TreeView (Nodes collection) remain the same (1) and when i click on the duplicated root nodes and fall into debug (via my MyTreeView_AfterSelect event/routine), I can tell from the tag ID/hex handle that it is in fact the same (single) root node. It's just being displayed twice in the visual aspect of the TreeView.
This tells me that there is a bug in the Treeview control itself. I have researched this issue online in previous StackOverflow articles (as well as in a few other forums) and I have tried a number of suggestions/approaches to try to get around the problem. To date, none have been successful for me.
Any suggestions/insights would, of course, be greatly appreciated. I am at a loss as to what to do next.
Regards,
Christopher H. Fleetwood

I agree with Hel O'Ween, the issue is probably with the way its coded.
The issue I think is with the command "TreeView.Nodes.Clear()" , it will Delete the "RootNode" as well as all other nodes and leave a "Blank" Duplicate entry in the system. If you run it again it will produce another Blank "Rootnode" entry which will cause havoc with you application as you have found.
The way to deal with it is to make the Root-Node Permanent. (It can be Set to ReadOnly - Hidden) Then just clear down the Childnodes that are attached to it.
Here is the vbcode i use to clear a treeview of all child nodes.
Dim lngCount As Integer
For lngCount = TreeView.Nodes.Count To 1 Step -1
If lngCount - 1 = 0 Then Exit Sub
TreeView.SelectedNode = TreeView.Nodes.Item(lngCount - 1)
TreeView.Nodes.RemoveAt(lngCount - 1)
Next
I did see an article on it once about never deleting the rootnode or you'll get a blank entry left! If i find it i'll add a link. It might only be vb.net with this issue.

Don't pass TreeNode objects ByRef.

Related

Outlook Instant Search - hit and miss results

I have the below code to run an instant search from a tool I've developed that iterates through all Outlook folders and then uses the restrict method to get two counts, first the total and the second, a count of those items that are two years or older.
Once done, this is displayed to the user in a listview and the code below is supposed to do an instant search query on the selected result using the 'received' date to limit the results.
What I've found is that sometimes the instant result filters the results and in other cases it purely displays all items from within the selected folder. For example, a folder has 90 items but 5 are over 2 years old, sometimes it will show 5 (generally after the selection has been made from the listview twice) and the rest of the time the full 90 are shown.
Has anyone else come across this is and managed to resolve it?
Private Sub OpenOlFolder(sender As Object, e As EventArgs) Handles lvwProgress.DoubleClick
With olApp.ActiveExplorer
'// CLEAR SEARCH
.ClearSearch()
'// SWITCH TO SELECTED FOLDER
.CurrentFolder = GetOlFolderFromPath(Me.lvwProgress.Items(Me.lvwProgress.FocusedItem.Index).SubItems(0).Text)
'// DO SEARCH
.Search(String.Concat("received:<", RetentionDate.ToString("MM/dd/yyyy")), Outlook.OlSearchScope.olSearchScopeCurrentFolder)
End With
End Sub
In your code I have noticed multiple dots in the single line of code. It is hard to understand where your code fails if something unexpected happens. So, breaking the chain of property and method calls is essential in troubleshooting such cases.
In the code you change the current folder by setting the CurrentFolder property of the Explorer class. It is a time-consuming operation, so it make sense to wait until it is done. For example, you may try to run the Search method in the Explorer.FolderSwitch event which is fired when when the explorer goes to a new folder, either as a result of user action or through program code.
Also it make sense to do the same operation manually to make sure the filter is correct.

How to change the image displayed on a button at runtime in Visual Basic?

I am trying to change the Image attribute associated with a button when the button is clicked.
I have added the image I want to display (called "Black Pawn.png") as a resource within the solution and (from searching around and looking at similar questions) am trying to set the image attribute to it as shown below:
Private Sub boardButtonA2_Click(sender As Object, e As EventArgs) Handles boardButtonA2.Click
Dim sprites As Object = My.Resources.ResourceManager
boardButtonA2.Image = sprites.GetObject("Black Pawn.png")
End Sub
But when I click the button, the default image on it just disappears and is replaced with nothing instead of Black Pawn.png.
I am pretty new to using Visual Basic and Visual Studio so I may have failed to have added the image as a resource properly but I can see the image in the Solution Explorer above the form the button is in.
Any help or advice would be a great help.
Thanks.
EDIT:
Having thought more about the potential scenario, I'm wondering whether this answer is actually relevant. Your example code uses a hard-coded String but I'm wondering whether the actual String really would be a user selection. I'll leave it here anyway.
ORIGINAL:
There's no reason to use a hard-coded String to get a resource. If the String was from user entry then maybe, depending on the circumstances. As it is though, you should be using the dedicated property for that resource. That means using this:
boardButtonA2.Image = My.Resources.Black_Pawn
Just be aware that a new object is created every time you get a resource from My.Resources. For that reason, don't keep getting the same property over and over. If you need to use a resource multiple times, get it once and assign it to a field, then use that field multiple times, e.g.
Private blackPawnImage As Image
Private Function GetBlackPawnImage() As Image
If blackPawnImage Is Nothing Then
blackPawnImage = My.Resources.Black_Pawn
End If
Return blackPawnImage
End Function
and then:
boardButtonA2.Image = GetBlackPawnImage()
Also, I suggest that you change the name of the property to BlackPawn rather than Black_Pawn. You can change it to whatever you want on the Resources page of the project properties.
EDIT:
If this application is a chess game then you definitely will need every resource image for the playing pieces so you probably ought to get all of them at load and assign them to variables, then use them from those variables over the course of the game.
(I am assuming the question is for WinForms, if it isn't I will remove this answer)
When adding images as resources to a project, you have to pay attention to the name given to the resource after it is imported - the name can be changed if you want.
The image below is from one of my projects:
The name of the files on disk are:
CSV - Excel.png
GreenDot.png
RedDot.png
To fix your problem change the line:
boardButtonA2.Image = sprites.GetObject("Black Pawn.png")
to be:
boardButtonA2.Image = sprites.GetObject("Black_Pawn")
I don't think adding the image by going into Project > Add Existing Item properly added the image as a resource.
I instead went into *Project > (Solution Name) Properties > Resources > Images > Add Existing Item * and added it that way and was then able to get it working using jmcilhinney's method (I think my original method/a variation of it would work too but theirs is better).

Fundamental Excel VBA Classes & Objects

There is something I am not understanding and I guess it must be pretty basic.
Can someone please be so kind to explain to me the relationship between Classes and Objects enough so that I can understand what is happening below?
Both Location and Name are properties of the PivotTable Class and return strings.
Why do the first statements work but the last 4 give the error
"Object doesn't support this action (Error 445)"?
?ActiveWorkbook.ActiveSheet.PivotTables.Count
3
?ActiveWorkbook.ActiveSheet.PivotTables(1).Name
PivotTable12
?ActiveWorkbook.ActiveSheet.PivotTables(2).Name
PivotTable3
?ActiveWorkbook.ActiveSheet.PivotTables(3).Name
PivotTable2
?ActiveSheet.PivotTables(1).Location
?ActiveSheet.PivotTables(2).Location
?ActiveSheet.PivotTables(3).Location
?ActiveSheet.PivotTables("PivotTable12").Location
[Location Def][1]
[Immediate Window][2]
[Error][3]
Some objects have properties or methods which either aren't available in every version, or in every situation. Seemingly most tasks in VBA can be accomplished in multiple ways; this, it's important to be comfortable with looking for "alternative means of accomplishing the same thing."
In this case my next step would be to check out which properties/methods are available for my object in my scenario by Setting it to a variable:
Dim p As PivotTable
Set p = ActiveSheet.PivotTables(1)
Stop
Run that code, and when execution breaks at the Stop, double click the variable p to select it, right-click it, click Add Watch... and click OK.
This will open the Watches. Use the ⊞ to open and explore the tree to see what's available in this context.
Location likely says "Application Defined error..." however perhaps you can find the location with:
?p.DataBodyRange.Cells.Address
or
?p.DataLabelRange.Cells.Address
...see what the Watches window shows as available within the context you're using.

My.Settings "Destination array was not long enough. Check destIndex and length, and the array's lower bounds."

I have two forms with combo boxes. The combo box values are stored in My.Settings.testDevices. (System.Collections.Specialized.String.Collection) with a scope of User.
The second form adds the ability to add items to testDevices, and then upon exit it updates My.Settings.testDevices.
Now, only if I make a change to the settings (adding items only), when I exit back to the main form (which remains loaded throughout the process), my application crashes with the following message:
"Additional information: Destination array was not long enough. Check destIndex and length, and the array's lower bounds."
As I understand it, this might be a concurrency issue, however I'm not sure.
My code:
In my main form Load event: (to load from My.Settings)
testDevicesComboBoxMain.Items.Clear()
My.Settings.testDevices.CopyTo(mainFormTestDevices, 0)
testDevicesComboBoxMain.Items.AddRange(mainFormTestDevices)
Where "testDevicesComboBoxMain" is the combo box on the main form.
On the secondary form Close Event: (to save to My.Settings)
Dim items(testDevicesComboBox.Items.Count - 1) As String
testDevicesComboBox.Items.CopyTo(items, 0)
My.Settings.testDevices.Clear()
My.Settings.testDevices.AddRange(items)
My.Settings.Save()
I have found similar questions on here, but none with answers that I understand :P
As I am a beginner with vb.net, could any answers be provided in an easy to understand form please!
Thanks.
I forgot to add:
Public items(My.Settings.testDevices.Count - 1) As String
Public mainFormTestDevices(My.Settings.testDevices.Count - 1) As String
I tried setting separate declarations just in case there was some kind of conflict. These obviously do the same thing, just with different names.
I fixed it by adding a For loop to read from My.Settings.
For Each i As String In My.Settings.testDevices
testDevicesComboBoxMain.Items.Add(i)
Next
This seems to have cured the problem, and may perhaps be a more "modern" way of doing it?

Progress bar for populating treeview (wrong thread error)

I have a recursive function with some loops that I use to populate a TreeView with a list of files and folders. In some situations this can take a bit of time to execute especially when loading a lot of files.
I wish to show a progress bar to show how far through the loading process I am.
I have searched everywhere for this and tried with a BackgroundWorker and am now very lost.
Sorry I'm not really sure what code to share but please let me know if you need to see any of the functions/routines.
The error I get with the background worker is a System.InvalidOperationException
"Action being performed on this control is being called from the wrong thread."
Background Worker_DoWork:
For i = 0 To 100
PopulateTreeView()
BackgrondWorked.ReportProgress(i)
Next
PopulateTreeView Sub:
Dim node As TreeNode = New TreeNode
node.Text = "Music"
TreeView.Nodes.Add(node)
GetFilesAndFolders("D:\Music", node)
This throws the error described above when trying to add the first node with text "Music"
Any suggestions will be greatly appreciated. Thanks.