问题
This is a Long one, but stay with me...
I have a Dictionary that saves "PO" as Key and "SO" as Items (there can be cases that a certain "PO" has multiple "SO".
My Excel data in a worksheet, where the Dictionary get's his values looks like this:
The code for populating the Dictionary (working) looks like this:
Option Explicit
Const OrdersDBShtName As String = "Orders_DB"
Public OrdersDBSht As Worksheet
Public LastSORow As Long
Public PODict As Object ' Public Dictionay for PO#, and keep SO per PO# (unique ID)
'======================================================================
Sub InitDict()
Dim AdminSht As Worksheet
Dim i As Long, j As Long
' set the sheet object where the "Orders_DB" data lies
On Error Resume Next
Set OrdersDBSht = ThisWorkbook.Worksheets(OrdersDBShtName)
On Error GoTo 0
If OrdersDBSht Is Nothing Then ' in case someone renamed the "Admin" Sheet
MsgBox Chr(34) & OrdersDBShtName & Chr(34) & " Sheet has been renamed, please modify it", vbCritical
End
End If
With OrdersDBSht
LastSORow = .Cells(.Rows.Count, "B").End(xlUp).Row ' get last row with data in column "B" ("SO#")
' get all SO numbers in Dictionary (SO# is unique ID)
Set SODict = CreateObject("Scripting.Dictionary")
' get all PO's in Dictionary (PO# is unique, but there can be several SO# per PO#)
Set PODict = CreateObject("Scripting.Dictionary")
Dim ID As Variant, Names As String
' add unique Category values to Dictionary object , and save Item Names in Names
For i = 2 To LastSORow
If Not PODict.Exists(.Range("A" & i).Value) Then
ID = .Range("A" & i).Value
For j = 2 To LastSORow
If .Range("A" & j).Value = ID Then
If Names = "" Then
Names = .Range("B" & j).Value ' get the SO#
Else
Names = Names & "," & .Range("B" & j).Value ' get the SO#
End If
End If
Next j
PODict.Add ID, Names
End If
ID = Empty
Names = Empty
Next i
' section below for DEBUG Only (works)
Dim Key As Variant
For Each Key In PODict.keys
Debug.Print Key & " | " & PODict(Key)
Next Key
End With
End Sub
The Problem: I have a User_Form with 2 ListBoxes.
ExistingPO_LB- aListBoxfor "PO"s, reads all the UniqueKeys in theDictionaryobject.ExistingSO_LB- aListBoxfor "SO#", should show onlyItemsfor theKeyselected inExistingPO_LB.
In some cases (like the screen-shot below) it works:
In some cases (like the screen-shot below) it doesn't (even though the Items have been saved correctly in PODict Dictionary object):
User_Form Code
Private Sub EditSO_Btn_Click()
With Me.ExistingSO_LB
For i = 0 To .ListCount - 1
If .Selected(i) Then
EditSONumer = .List(i)
Exit For
End If
Next i
End With
If EditSONumer = 0 Then
MsgBox "No SO was selected from the list", vbInformation
Exit Sub
End If
Unload Me
AddEdit_Orders_Form.Show ' call sub Edit Order (load Add Order with the SO# data requested)
End Sub
'=========================================================
Private Sub ExistingPO_LB_Click()
' ****** This is the Sub I guess I'm missing something ******
Dim i As Long
Dim POSelected As Variant
Dim SOArr As Variant
With Me.ExistingPO_LB
For i = 0 To .ListCount - 1
If .Selected(i) Then
POSelected = .List(i)
Exit For
End If
Next i
End With
' update the SO listbox with only relevant SO (from the selected PO)
SOArr = Split(PODict(POSelected), ",") '<=== PODict(POSelected) return empty ???
With Me.ExistingSO_LB
.Clear ' clear the previous items
For i = LBound(SOArr) To UBound(SOArr)
.AddItem SOArr(i)
Next i
End With
End Sub
'=========================================================
Private Sub UserForm_Initialize()
' load all existing PO's from "Orders DB" sheet
Dim Key As Variant
' populate listbox with PO's
With Me.ExistingPO_LB
For Each Key In PODict.keys
.AddItem Key
Next Key
End With
End Sub
回答1:
The numeric keys were entered as numbers and you are fetching them as strings. I suggest that you stick to one convention for your dictionary.
Sub TestDict()
Dim dict As New Dictionary
dict.Add 1, "one"
dict.Add "2", "two"
Debug.Print dict("1") ' Nothing
Debug.Print dict(1) ' one
Debug.Print dict("2") ' two
Debug.Print dict(2) ' Nothing
End Sub
Solution
Chose a convention for your dictionary and stick to it. In this application I would take the convention of always converting my keys to strings, both when inserting and when fetching. A few changes in your code can achieve it:
If Not PODict.Exists(CStr(Range("A" & i).Value) Then ' could use .Text also
PODict.Add CStr(ID), Names
SOArr = Split(PODict(CStr(POSelected)), ",") ' maybe not needed here, but to illustrate
来源:https://stackoverflow.com/questions/44787615/dictionary-doesnt-display-items-for-certain-key-numeric-value