VBA Extra key/item pairs in scripting.dictionary - vba

This code was working for several days, looping through a dictionary just fine. The loop started breaking, and I have extra keys in the dictionary and I have no idea where they came from.
I am creating a scripting.dictionary with the following:
Dim riskDict As New Scripting.Dictionary
After which, the object is empty, as seen in the watch:
I then add my first key/item pair:
riskDict.Add "Weight", Array("WP", 0)
And after running this one line of code, I now have this:
Where did these two extra keys (Item 2 and 3) come from?! This is a problem, since later in my code I use:
For Each key In riskDict
temp = riskDict(key)
...
Next key
And this loop breaks, since it starts referring to keys which are empty. This does not seem to have been happening until just now, and the code has been running fine for a few days. (I changed some things elsewhere in the code, but completely unrelated to this.)
Let me know if I am doing anything ridiculous, or missing something obvious, and thanks!

Make riskDict private or clear your array before using it. Hard to tell what your doing with it since there is critical elements missing.
Dim riskDict As New Scripting.Dictionary

Although this is an old question, it might still be of interest to other users.
Before the first item has been added to an initialized dictionary object, the local window of the VBA editor must not be switched on if a breakpoint exists in the scope of this Dictionary object. Otherwise a key-value pair (0, Empty) might be inserted into the dictionary. This dictionary entry cannot be deleted again in the immediate window, even Dictionary.RemoveAll fails. This is the case even if the local window has been closed again in the meantime.
The exact circumstances under which this behavior occurs are not clear to me: I've only observed this behavior of Dictionary objects in class modules so far, but on the same line of code it sometimes occurs, sometimes not.
This SO discussion also describes the behavior when a watch is set on the Dictionary object. There, this behavior is associated with the property of dictionaries that when a query is made for an unknown key, this key is automatically added with a value of 'Empty'.

Related

VBA ShapeRange.Rotation Property randomly stops working

I am trying to rotate a shape. Below is the relevant snippet.
Sheets("Sheet1").Shapes.Range(Array("Down Arrow 8")).Select
Selection.ShapeRange.Rotation = 90 + Sheets("Sheet2").Range("H8")
My problem is that the last line randomly works and randomly doesn't! I used to have it as a number (instead of a to string) but this refuses to work! It worked fine, then I made some changes to the macro and then it helpfully returned Error 438 - Object doesn't support this property or method. I fiddled around, debugged, etc (without ever changing the code) and it started working! Then I made some more changes and it stopped working and it seems to have stopped working for good. I literally copied this code from a recorded macro.
What I have tried:
Using CStr() to convert the value to a string
Declaring a variable and using the variable
What I really don't understand is why it would work without me changing the code and them stop working without me changing the code (i.e. the relevant lines)
A few things. First, avoid select. It isn't necessary and will save you a lot of headache and time in the future. Second, you can reference the shape by its name right from the Shapes object, assuming you are only intending to rotate one shape with the code. Finally, are you certain the value in Sheets("Sheet2").Range("H8") is numeric? If not, this could cause an error. The code below handles the first two issues. Beyond that, we'll need to see more code to determine the error.
Sheets("Sheet1").Shapes("Down Arrow 8").Rotation = 90 + Sheets("Sheet2").Range("H8")

Change selected ComboBox item, from text file string not working

Ok.
So I am working on a project, irrelevant, and I have a bunch (8) of ComboBoxes (they are in DropDownList mode) and there is 8 save files. I have them being imported and converted to strings:
Using class2 As New StreamReader(path & "SaveData/classdata/classdata2.NIC")
Dim fdcount1 As String
fdcount = class2.ReadToEnd()
MessageBox.Show(fdcount1)
hr2choice.SelectedItem = fdcount1
End Using
I already tested this, and it seems to be working.
(Test code I used:)
MessageBox.Show(fdcount1)
and it showed the value ("DiVita")
Despite this, when I tried setting the ComboBox value to this, it did not seem to work.
The ComboBox does have this value in it, and if I try this, it works:
hr2choice.SelectedItem = "DiVita"
For whatever reasons though, it does not work when I try doing it directly from the string.
Thanks for any help with this!
Nic
To answer this, I have to assume that the data in the text file is formatted as one line for each piece of data.
There seems to be a couple of issues with your code. fdcount is just declared as a string where it should be an array to make it easier to access each line that is read from the file. fdcount1 has no relationship to fdcount - it is a completely separate entity, so the data in fdcount1 is coming from somewhere else.
Rather than the above code, It's easier to use this
Dim fdcount() As String
fdcount = File.ReadAllLines("SaveData/classdata/classdata2.NIC")
MessageBox.Show(fdcount(1))
Note that fdcount is declared as an Array of String. The 2nd line does all the opening, reading into the array, and closing of the file.
You can then access each element of the array as shown in the 3rd line.

Add a row using linq or sql in VB.NET

I am developing an application, and as one of the steps, I need to have a set of information backed up to an SQL database.
I don't have any experience using SQL, and was hoping I could find a simple command that would just add a row with the information i'd needed... But I don't understand much of the information I've found for it. I've even had issues just using the snippet provided within Visual Studio.
I tried using the snippet for adding a row, and here's the code that's produced.
The reference to RDataSet.ArchivedIncidentsDataTable keeps giving me an error message telling me "object reference to non-shared object requires an object reference", which to me, is one of the least helpful error messages you could possibly receive. I don't understand what it means, or what I need to do in order to fix it.
Here is the code that I have, which is basically just the snippet that's provided:
Private Sub Save_SQLBackup_Info()
Dim newRow = CType(RDataSet.ArchivedIncidentsDataTable.NewRow(), RDataSet.ArchivedIncidentsRow)
newRow.CustomerID = "A124"
newRow.CompanyName = "Acme"
RDataSet.ArchivedIncidentsDataTable.Rows.Add(newRow)
End Sub
I'm not sure how filling in the fields for this small amount of code produced an error.
Also, after this segment of code is run, don't I have to run a command to update the database? The guides I'd been following online all referenced something like that, but I didn't understand the other parts of them.
I'm sure the problem i'm having is a matter of "You didn't declare X.", however I don't know much about SQL... If that's even what I'm using.
I would appreciate some guidance.
All I need to do is add a row to the database using a few fields from the form.
I have an online SQL server linked as a data object, and the dataset is titled RDataSet , the data table is titled ArchivedIncidentsDataTable.
The few guides I've found online reference different parts, like data adapters, that I don't have, and / or don't reference how they got to that part... I'm completely lost.
Is there any sort of one-line command I can run that will just shoot the information I'd like added to a row into this database? I don't understand why it has to be this complicated...
EDIT:
One of the answers was to drag an instance of the dataset onto the form.
After doing this, and trying to reference it, I'm now getting an error message telling me :
"Access of shared member, constant member, enum member or nested type through an instance; qualifying expression will not be evaluated.
This message appears under the autocorrect options, and gives me the option to replace the dataset I added to the form, RDataSet1, with RDataset.
When hovering over the code "RDataSet1.ArchivedIncidentsDataTable.NewRow()", I still see the error message "Object Reference requires an object reference".
The message
object reference to non-shared object requires an object reference
means you're trying to access a member of a class like it's a shared member, but it's not, so you need an actually instance of that class (an object reference).
If you're using WinForms, you can just drag a RDataSet onto your Form, and it should generate a field in your Form called RDataSet1 or something like that (I asumme you used the DataSet Designer of Visual Studio).
Then use RDataSet1 (the object reference) instead of RDataSet (the type).

Proper procedure for declaring global variables in VBA?

I have a macro workbook with a number of worksheets that exist permanently, which are constantly cleared, updated, etc. Since they are referred to in various subroutines, I have made each corresponding worksheet object a pseudo-global variable in the following manner, for example for the "Main" sheet:
Function MAIN() As Worksheet
Set MAIN = ThisWorkbook.Sheets("Main")
End Function
By doing so, I can then refer to each sheet in the other subroutines, for example:
MAIN.Cells.ClearContents
I have also defined some pseudo-global constants which are located in a fixed place on the "Main" sheet in a similar way, for example:
Function NumLines() As Integer
NumLines = MAIN.Range("C3").Value
End Function
In this way, I use "NumLines" just like any variable throughout the code.
I expect that there is a more efficient way to manage globally accessed variables like these and was wondering, what would be a better way to accomplish this?
For reliable sheet reference I would suggest to use Sheet.CodeName Property. Each sheet has its unique CodeName which you could find in the place marked yellow on the picture below.
For quick reference to cell value I would suggest to use Range name. After you select you C3 cell you need to put unique name in the box marked yellow below. All Range names are unique in the workbook.
As a result you can use sheet and cell reference as presented below in each of your subroutines in your project.
Sub Test_Macro()
Debug.Print MAIN.Name '>> result: Sheet1
Debug.Print Range("CellC3").Value '>> result: 100
End Sub
I expect that there is a more efficient way to manage globally accessed variables like these and was wondering, what would be a better way to accomplish this?
When I use global variables in VBA, I do three things.
I always preface global variables with a g_ prefix. It seems often that a global variable in VBA is useful. But I've also spent far too long trying to track down "what variables are global or not?" in other people's code. Keeping a very clear naming convention will save you and whoever looks at your code a TON of hassle in the future.
This is even more important if you are less experienced as a developer. Avoiding globals is hard in VBA, and the less experience you have, the more likely it is you will use globals. For others to help or maintain the code this becomes so important.
If you are going to be using even a small number of global variables, you must use Option Explicit unless you want to cause nightmares in maintaining code. It's hard enough to track down these errors when you wrote code let alone months or years later.
I always create a module which is called "GlobalVariables" or something similar. That module contains all of the global declarations in one location. For larger code bases this can become longer but it has always paid off for me because I know exactly where all my globals are defined. None of the "which file is this variable actually being defined in?" game.
Just an unrelated note, too, in your first example - I would use the code name rather than that function. Each VBA worksheet has a sheet name ("Main" in your case) as well as a codename, which you can set in VBA and remains the same. This prevents users from changing the name of "Main" and breaking code.
You can also refer directly to them similar to how you are using MAIN.Cells. KazJaw has a good example of this.

Make Word's automatic spell-checking pick up dictionary changes

In MS Word it's possible to add words to a custom dictionary so they're recognized. If a word is not recognized, Word automatically puts a red squiggly line underneath it. If you add that word to the custom dictionary, this line disappears. What I'd like to do is perform this process automatically via a macro. It appears that one has to manually open the dictionary file and write the new word, as there is no method on the Word Dictionary object to add words to a given dictionary. This is no problem, except that Word doesn't automatically pick up the new word and remove the red squiggly lines underneath the newly-added word. I've even tried clearing the custom dictionaries and adding them back in, but it doesn't seem to reload the dictionary until you manually run a spell check. Sample code for this follows:
Dim x As Dictionary
Dim fname As String
fname = "C:\Users\me\AppData\Roaming\Microsoft\UProof\md.dic"
' code to add word to dictionary goes here
With CustomDictionaries
.ClearAll
.Add fname
.ActiveCustomDictionary = CustomDictionaries.Item(fname)
End With
Is there any way to make Word recognize the newly added word(s) in a custom dictionary without running the interactive spell check? It does this silently if you manually add words, but I can't seem to replicate this behavior in VBA. I'd like the red lines to go away automatically just like they do when you manually add words.
I haven't exactly solved the problem, but I think I figured out a work around. You can get a collection of Range objects which represent spelling errors using ActiveDocument.SpellingErrors. I'm going to search this collection for text matching the word I've added to the dictionary, and then set .NoProofing = True on the object. This appears to make the red lines go away, and having added the new word to the dictionary will prevent them from coming back the next time I open the document. I haven't fully tested this approach, but it looks promising.
EDIT
This approach is flawed, as additional instances of the word which are entered during the same session will have the red squigglies underneath them because they haven't been explicitly ignored and the spell check isn't yet using the updated dictionary. If you just pull up the custom dictionary dialog manually and click OK, something happens in the background to re-read the dictionary. I just can't figure out how to do this in code.