TreeView: Debugging Load Procedure

P

(PeteCresswell)

I'm loading a TreeView from a work table.

Doing it over-and-over again: same data, same code.

The problem seems to be that I'm trying to create a duplicate node (i.e. one
with the same .NodeKey as an existing node.

I've got error trapping code in there that spells it all out: the record ID of
the input row that it's choking on, the offending .NodeKey and so-forth.

So far, so good....

But it's abending on a different .NodeKey value each time I run it.

Doesn't seem right somehow.... as bad as I've probably messed up the coding it
seems like it should die on the same line of code (which it is) and the same
data (which it is not).

I'm resisting the urge to post the code because I'm fishing for something about
the TreeView that's different from other controls like the ListBox - maybe some
kind of latency in the node-building process? If somebody wants to see the
code, no problem... but it's ugly...

Anybody been here?
 
D

Douglas J. Steele

You can't reuse NodeKey values: they must be unique.

What's the error message you're getting. If you are using distinct Key
values, perhaps you'd better post the code.
 
P

(PeteCresswell)

Per Douglas J. Steele:
What's the error message you're getting. If you are using distinct Key
values, perhaps you'd better post the code.

As you've probably guessed: "35602: Key is not unique in collection"

My problem is that it seems like I should get that error for the same key value
each time I iterate through the table to load it.

Instead, it pops on a different record each time.

If there aren't any latency issues with the Tree's node collection, I guess I'll
have to put it down to something unusually dumb by Yours Truly.

Here's the code, but only because somebody asked for it. Any field that ends
in "...ID" is an autonumbered PK. Doesn't mean it's unique within the input
RS, though because the joins involved in the query that generates it create
multiple lines with similar values in some columns.

It's going out the window on line 1406.

===================================================
Public Function DealTreeLoad_ByDeal(ByVal theExpandAllSwitch As Boolean, ByVal
theSelectWhat As Long, ByVal theSortSpec As String, ByVal theCollateralManagerID
As Variant, ByVal theClosingDateFrom As Variant, ByVal theClosingDateTo As
Variant, ByVal theDealStatusID As Variant, ByVal theUnderwriterID As Variant,
ByRef theTree As Control, ByRef thePrvRootNode As Node) As Long

1000 debugStackPush mModuleName & ": DealTreeLoad_ByDeal"
1001 On Error GoTo DealTreeLoad_ByDeal_err

' PURPOSE: To load a tree control with deals and any component tranches,
making the
' highest level of the tree the DealName
' ACCEPTS: - Switch telling if all nodes SB expanded as they are created
' - ORDER BY specs for the sort statement. e.g. ".ClosingDate DESC,
..DealName ASC". The rest of the statement is in constant mDealTreeLoadSQL.
' - Pointer to the tree TB loaded
' - Pointer to a Node object used by the calling form to track the
last root node selected.
' This enables the calling form to collapse each node as the user
moves to the
' next root node.
' - Optional value for ID of CollateralManager tb selected
' - Optional value for "From" ClosingDate range tb selected
' - Optional value for "To" ClosingDate range tb selected
' - Optional value for ID of Deal's Status tb selected
' - Optional value for ID of Underwriter tb selected
' - Optional switch telling to select all records
' - Optional switch that requests sort by ascending ClosingDate
' - Optional switch that requests sort by descending ClosingDate
' RETURNS: Number of nodes loaded
'
' NOTES: 1) The calling routine is responsible for not sending us more
nodes than the tree control
' can handle. See "mNodeLimit" constant.
' 2) This code can handle up to six levels of indentation.
' It cannot handle infinite because we wimped out on the *real*
solution -
' hard-coding it here instead of dynamically modifying SQL
depending on # of children.
' We have genericized to "NameLev1", "NameLev2".... and so-forth.
' In this case, the deal's name is "NameLev1" and each tranch's
name is "NameLev2"... which
' must be assigned by the input query.
' 3) THIS CODE IS INTERTWINED WITH THE INPUT QUERY. You cannot
understand this code unless you
' can see the input query. Especially note that the
mNameLevelLit... constants relate to
' certain columns in the query and are used when
constructing/decoding node keys.
' 3) At least one optional parm must be specified - we use it to
determine which query to
' base the process on.
'


1002 Dim thisDB As DAO.Database
Dim inputRS As DAO.Recordset
Dim myQuery As DAO.QueryDef
Dim curNode As Node
Dim myDebugNode As Node

Dim i As Integer
Dim curDealID As Long
Dim curRecID As Long
Dim curNodeText As String
Dim curParentKey As String

Dim prvNameLev1 As String
Dim prvNameLev2 As String
Dim prvNameLev3 As String
Dim prvNameLev4 As String
Dim prvNameLev5 As String
' Dim prvNameLev6 As String

Dim curNodeKeyLev1 As String
Dim curNodeKeyLev2 As String
Dim curNodeKeyLev3 As String
Dim curNodeKeyLev4 As String
Dim curNodeKeyLev5 As String
Dim curNodeKeyLev6 As String 'Not currently used, but needed for
BugAlert

Dim curNodeTagLev1 As String
Dim curNodeTagLev2 As String
Dim curNodeTagLev3 As String
Dim curNodeTagLev4 As String
Dim curNodeTagLev5 As String
' Dim curNodeTagLev6 As String

Dim itemCount As Long 'Strictly for debugging
Dim curNameLev1 As String
Dim curNameLev2 As String
Dim curNameLev3 As String
Dim curNameLev4 As String
Dim curNameLev5 As String
Dim curNameLev6 As String 'Not currently used, but needed for
BugAlert
Dim curRecIdList As String

Dim myNodeKeyRoot As String

Dim wantRecord As Boolean
Dim myLev1Count As Long 'The number of deals loaded.

Dim myRecsLoaded As Long 'number of records successfully loaded -
always less than node count BC one field generates at least 2 nodes
Dim okToProceed As Boolean
Dim myNodeCount As Long 'total number of nodes loaded
Dim myInputCount As Long 'total number of records in input RS
Dim myInputEstimate As Long
Dim mySQL As String
Dim curErr As Long

1003 DoCmd.Hourglass True
1004 StatusSet "Loading deals..."
1005 DoCmd.Echo False
1006 DoEvents
1009 Set thisDB = curDB()

'
----------------------------------------------------------------------------------
' Determine which query we are going to use to load the temp table

1010 Select Case theSelectWhat
Case gSelect_All
1021 Set myQuery = thisDB.QueryDefs("qryDealTreeLoad_AllDeals")
1029 okToProceed = True

1030 Case gSelect_CollateralManager
1031 If IsNumeric(theCollateralManagerID) Then
1032 Set myQuery =
thisDB.QueryDefs("qryDealTreeLoad_SelectedCollateralManager")
1033 myQuery.Parameters("theCollateralManagerID") =
theCollateralManagerID
1034 okToProceed = True
1035 Else
1036 MsgBox "You did not specify a collateral manager.", vbExclamation,
"Cannot Load"
1039 End If


1040 Case gSelect_ClosingDate
1041 If ((IsDate(theClosingDateFrom) = True) And (IsDate(theClosingDateTo)
= True)) Then
1042 Set myQuery =
thisDB.QueryDefs("qryDealTreeLoad_SelectedClosingDateRange")
1043 With myQuery
1044 .Parameters("theDateFrom") = theClosingDateFrom
1049 .Parameters("theDateTo") = theClosingDateTo
1050 End With
1051 okToProceed = True
1052 Else
1053 MsgBox "One or both closing dates illegal: From='" &
theClosingDateFrom & ", To='" & theClosingDateTo & "'.", vbExclamation, "Cannot
Load"
1059 End If


1060 Case gSelect_Status_Deal
1061 Set myQuery = thisDB.QueryDefs("qryDealTreeLoad_SelectedDealStatus")
1069 okToProceed = True


1070 Case gSelect_Underwriter
1071 If IsNumeric(theUnderwriterID) Then
1072 Set myQuery =
thisDB.QueryDefs("qryDealTreeLoad_SelectedUnderwriter")
1073 myQuery.Parameters("theUnderwriterID") = theUnderwriterID
1074 okToProceed = True
1075 Else
1076 MsgBox "You did not specify an underwriter", vbExclamation,
"Cannot Load"
1079 End If

1080 Case Else
1089 BugAlert True, "Unexpected Select Spec=' & theselectwhat & " '."
1099 End Select

'
----------------------------------------------------------------------------------
' Create the work table and open up a recordset into it

1100 If okToProceed = True Then
1101 okToProceed = False
1102 WorkTable_Create "ttblDealTreeLoad", "zmtblDealTreeLoad"
1103 myQuery.Execute dbFailOnError 'Populate
work table depending on what selection query used
1104 mySQL = mDealTreeLoadSQL & theSortSpec
1105 Set inputRS = CurrentDb.OpenRecordset(mySQL, dbOpenDynaset) 'Open up
RecordSet into work table that is sorted as specified
1109 With inputRS
1110 If Not ((.BOF = True) And (.EOF = True)) Then
1111 okToProceed = True
1112 End If
1113 End With
1119 End If

'
----------------------------------------------------------------------------------
' Initialize the tree, then load it from the temp table we just created

1130 If okToProceed = True Then
1131 With theTree
1132 .Nodes.clear
1133 .Checkboxes = False
1134 .Indentation = 0
1135 .LineStyle = 1
1136 .Scroll = True
1139 .Sorted = False 'MUY
IMPORTANTE: Otherwise tree will be sorted by NameLev1
1140 .Style = 6
'1121 myNodeKeyRoot = mTreeTagDelimiter & "000" & mTreeTagDelimiter & "0"
'1122 Set curNode = .Nodes.Add(, , myNodeKeyRoot, "Deals")
1149 End With

'1130 curNode.Expanded = theExpandAllSwitch

1150 With inputRS
1151 If Not ((.BOF = True) And (.EOF = True)) Then
1153 .MoveLast
1154 myInputCount = .RecordCount
1159 SysCmd acSysCmdInitMeter, "Loading " & Format$(.RecordCount,
"#,##0") & " nodes...", myInputCount

1170 .MoveFirst
1171 Do Until .EOF = True
1172 curRecID = !LocalRecordID
1173 curDealID = !DealID
1174 itemCount = itemCount + 1
1175 curNameLev1 = Trim$(!NameLev1 & "")
1176 curNameLev2 = Trim$(!NameLev2 & "")
1177 curNameLev3 = Trim$(!NameLev3 & "")
1178 curNameLev4 = Trim$(!NameLev4 & "")
1179 curNameLev5 = Trim$(!NameLev5 & "")
'1179 curNameLev6 = Trim$(!NameLev6 & "")

1200 curRecIdList = mTreeTagDelimiter & CStr(!AttachmentID & "") &
mTreeTagDelimiter & CStr(!CollateralManagerID & "") & mTreeTagDelimiter &
CStr(!DealID & "") & mTreeTagDelimiter & CStr(!TrancheID & "") &
mTreeTagDelimiter & CStr(!UnderwriterID & "")

' -------------------------------------
' Deal record

1210 If curNameLev1 <> prvNameLev1 Then
1211 prvNameLev1 = curNameLev1
1212 myLev1Count = myLev1Count + 1
1213 DoEvents
1214 curNodeKeyLev1 = gNodeType_DealRec & !DealID
1215 curNodeTagLev1 = gNodeType_DealRec & curRecIdList
' NB: We are having trouble when we load too much into the top node's text.
' The tree winds up scrolled to the right just enough to hide the plus
and minus
' signs of the topmost nodes.
'1216 curNodeText = curNameLev1
1216 curNodeText = DealNode_TextCreate(curNameLev1,
!ClosingDate_Deal, !CollateralTypeShort, !CollateralManagerName,
!SizeCurrent_Deal, !SizeCurrentDate_Deal, !SizeOriginal_Deal, !StatusName_Deal,
!UnderwriterName)
On Error Resume Next
Set curNode = theTree.Nodes.Add(, , curNodeKeyLev1,
curNodeText)
'xxx curNode.Sorted = False
curErr = Err
On Error GoTo DealTreeLoad_ByDeal_err

1218 curNode.Tag = curNodeTagLev1
1219 curNode.Expanded = theExpandAllSwitch
1220 myNodeCount = myNodeCount + 1
1221 prvNameLev2 = "" 'Neccessary to
handle theoretical situations where the same deal name appears in different
consecutive tables
1222 prvNameLev3 = ""
1223 prvNameLev4 = ""
1224 prvNameLev5 = ""
'1224 prvNameLev6 = ""
1229 End If

' -------------------------------------
' Tranche Header

1230 If ((curNameLev2 said:
1231 prvNameLev2 = curNameLev2
1232 If Len(curNameLev2 & "") > 0 Then
1233 curNodeKeyLev2 = gNodeType_TrancheHeader & curRecIdList
1234 curNodeTagLev2 = gNodeType_TrancheHeader & curRecIdList
1235 curNodeText = curNameLev2
1236 Set curNode = theTree.Nodes.Add(curNodeKeyLev1, tvwChild,
curNodeKeyLev2, curNodeText)
1237 curNode.Tag = curNodeTagLev2
'xxx 1238 curNode.Sorted = False
1239 curNode.Expanded = theExpandAllSwitch
1240 myNodeCount = myNodeCount + 1
1241 End If
1242 prvNameLev3 = ""
1243 prvNameLev4 = ""
1244 prvNameLev5 = ""
'1249 prvNameLev6 = ""
1299 End If

' -------------------------------------
' Attachment Header

1300 If ((curNameLev3 <> prvNameLev3) And (Val(!AttachmentCount &
"") > 0)) Then
1301 prvNameLev3 = curNameLev3
1302 If Len(curNameLev3 & "") > 0 Then
1303 curNodeKeyLev3 = gNodeType_AttachmentHeader &
curRecIdList
1304 curNodeTagLev3 = gNodeType_AttachmentHeader &
curRecIdList
1305 curNodeText = curNameLev3
1306 Set curNode = theTree.Nodes.Add(curNodeKeyLev1, tvwChild,
curNodeKeyLev3, curNodeText)
1307 curNode.Tag = curNodeKeyLev3
'xxx 1308 curNode.Sorted = False
1309 curNode.Expanded = theExpandAllSwitch
1310 myNodeCount = myNodeCount + 1
1311 End If
1312 prvNameLev4 = ""
1313 prvNameLev5 = ""
'1319 prvNameLev6 = ""
1399 End If

' -------------------------------------
' Tranche Record

1400 If ((curNameLev4 <> prvNameLev4) And (Len(curNameLev4 & "") >
0)) Then
1401 prvNameLev4 = curNameLev4
1402 If Len(curNameLev4 & "") > 0 Then
1403 curNodeKeyLev4 = gNodeType_TrancheRec & !TrancheID
1404 curNodeTagLev4 = gNodeType_TrancheRec & curRecIdList
1405 curNodeText = curNameLev4

' On Error Resume Next
1406 Set curNode = theTree.Nodes.Add(curNodeKeyLev2, tvwChild,
curNodeKeyLev4, curNodeText)
' curErr = Err
' On Error GoTo DealTreeLoad_ByDeal_err
'
' If curErr = 0 Then
1407 curNode.Tag = curNodeTagLev4
1408 curNode.Expanded = theExpandAllSwitch
'1409 Else
'1410 Set myDebugNode = theTree.Nodes(curNodeKeyLev4)
'1411 BugAlert True, "Tranch node insert failed for key '"
& curNodeKeyLev4 & "'." & vbCrLf & vbCrLf & "There is already a node with
text='" & myDebugNode.Text & "'. It's parent is '" & myDebugNode.Parent & "'."
'1412 End If

1413 myNodeCount = myNodeCount + 1
1414 End If
1415 prvNameLev5 = ""
'1413 prvNameLev6 = ""
1419 End If


' -------------------------------------
' Attachment Record

1500 If curNameLev5 <> prvNameLev5 Then
1501 prvNameLev5 = curNameLev5
1502 If Len(curNameLev5 & "") > 0 Then
1503 curNodeKeyLev5 = gNodeType_AttachmentRec & !AttachmentID
1504 curNodeTagLev5 = gNodeType_AttachmentRec & curRecIdList
1509 curNodeText = curNameLev5
On Error Resume Next
Set curNode = theTree.Nodes.Add(curNodeKeyLev3, tvwChild,
curNodeKeyLev5, curNodeText)

Select Case Err
Case 0
On Error GoTo DealTreeLoad_ByDeal_err
1510 myNodeCount = myNodeCount + 1
1519 curNode.Tag = curNodeTagLev5

Case mGotDupeKey
On Error GoTo DealTreeLoad_ByDeal_err
1520 myNodeCount = myNodeCount + 1 'Just
increment the count. Because of the nature of the input query, we expect
duplicate attachments and just ignore them

Case Else
On Error GoTo DealTreeLoad_ByDeal_err
1530 BugAlert True, "Line 1505"
End Select
'xxx curNode.Sorted = False
curNode.Expanded = theExpandAllSwitch
On Error GoTo DealTreeLoad_ByDeal_err
1590 End If
'1512 prvNameLev6 = ""
1599 End If

' -------------------------------------
' (NOT USED
'1600 If curNameLev6 <> prvNameLev6 Then
'1601 prvNameLev6 = curNameLev6
'1602 If Len(curNameLev6 & "") > 0 Then
'1603 curNodeKeyLev6 = mTreeTagDelimiter & CStr(!DealID) &
mTreeTagDelimiter & "6"
'1604 curNodeTagLev6 = mTreeTagDelimiter & CStr(!DealID) &
mTreeTagDelimiter & "6"
'1605 curNodeText = curNameLev6
'1606 Set curNode = theTree.Nodes.Add(curNodeKeyLev5,
tvwChild, curNodeKeyLev6, curNodeText)
'1607 curNode.Tag = curNodeTagLev6
'1609 curNode.Expanded = theExpandAllSwitch
'1610 myNodeCount = myNodeCount + 1
'1611 End If
'1619 End If


'1900 curNode.Expanded = False
1911 If myNodeCount > 1000 Then
1912 myInputEstimate = (myNodeCount / myRecsLoaded) *
myInputCount

1913 If myInputEstimate > mNodeLimit Then
1914 MsgBox "The TreeView control cannot handle more than " &
Format$(mNodeLimit, "#,##0") & " nodes." & vbCrLf & vbCrLf & "At the rate we're
going, the currend selection will create " & Format$(myInputEstimate, "#,##0") &
"." & vbCrLf & vbCrLf & "Change your selection criteria and try again.",
vbExclamation, "Too Many Items - We're Not Going To Make It"
1915 Exit Do
1916 End If
1919 End If

1920 If myNodeCount > mNodeLimit - 6 Then
1921 MsgBox "The TreeView control cannot handle more than " &
Format$(mNodeLimit, "#,##0") & " nodes." & vbCrLf & vbCrLf & "Your selection has
already created " & Format$(myNodeCount, "#,##0") & " nodes - and the end is not
in sight." & vbCrLf & vbCrLf & "Change your selection criteria and try again.",
vbExclamation, "Too Many Items - Won't Fit"
1922 Exit Do
1929 End If

1930 myRecsLoaded = myRecsLoaded + 1
1931 .MoveNext
1932 SysCmd acSysCmdUpdateMeter, myRecsLoaded
1939 Loop

1940 End If
1941 SysCmd acSysCmdRemoveMeter
1949 End With

1950 With theTree.Nodes(1)
1951 .EnsureVisible
1952 .Selected = True
'1953 .Expanded = True
1959 End With

1960 Set thePrvRootNode = theTree.Nodes(1)
1961 With theTree
1962 .Font = "Courier New"
1969 End With

1994 DealTreeLoad_ByDeal = myLev1Count
1995 End If

1996 With DoCmd
1997 .Echo True
1998 .Hourglass False
1999 End With

DealTreeLoad_ByDeal_xit:
DebugStackPop
On Error Resume Next
Set curNode = Nothing
Set myQuery = Nothing
inputRS.Close
Set inputRS = Nothing
Set thisDB = Nothing
Exit Function

DealTreeLoad_ByDeal_err:
BugAlert True, "RecID='" & curRecID & ", ItemCount=" & Format$(itemCount, "0")
& "', curNodeKeyLev1='" & curNodeKeyLev1 & "', curNameLev1='" & curNameLev1 &
"', curNodeKeyLev2='" & curNodeKeyLev2 & "', curNameLev2='" & curNameLev2 & "',
curNodeKeyLev3='" & curNodeKeyLev3 & "', curNameLev3='" & curNameLev3 & "',
curNodeKeyLev4='" & curNodeKeyLev4 & "', curNameLev4='" & curNameLev4 & "',
curNodeKeyLev5='" & curNodeKeyLev5 & "', curNameLev5='" & curNameLev5 & "',
curNodeKeyLev6='" & curNodeKeyLev6 & ", curNameLev6='" & curNameLev6 & "',
DealID='" & Format$(curDealID, "0") & ", Query used to build work table='" &
myQuery.Name & ", ."
Resume DealTreeLoad_ByDeal_xit
End Function
==========================================
 
P

(PeteCresswell)

Per (PeteCresswell):
Here's the code, but only because somebody asked for it.

For the truly masochistic, I'll start right now on concocting a little
self-contained app that encapsulates the problem - which I can email to anybody
who is interested.
 
P

(PeteCresswell)

Per (PeteCresswell):
For the truly masochistic, I'll start right now on concocting a little
self-contained app that encapsulates the problem - which I can email to anybody
who is interested.

Got the standalone, but guess what? Now the error has stopped.

When I first started testing the standalone, I was getting the error with a
funny little twist in it: Control being passed back to the calling routine
before the error trapping could display all the debugging info.

But after a few compacts, I can no longer provoke the problem - lending some
support to my current suspicion that something is goofy with the DB in general
or the TreeView in particular.
 
D

Douglas J. Steele

Okay, I'll assume you're good to go then, and not try to wade through all
that code!

(PeteCresswell) said:
Per (PeteCresswell):

Got the standalone, but guess what? Now the error has stopped.

When I first started testing the standalone, I was getting the error with
a
funny little twist in it: Control being passed back to the calling routine
before the error trapping could display all the debugging info.

But after a few compacts, I can no longer provoke the problem - lending
some
support to my current suspicion that something is goofy with the DB in
general
or the TreeView in particular.

Post back if the problem recurs.
 
P

(PeteCresswell)

Per Douglas J. Steele:
Okay, I'll assume you're good to go then, and not try to wade through all
that code!

Far from it. The "real" app is still doing it....

I'll flip you a sample mini-app when I can replicate the problem.

Meanwhile, I started writing the key value to a text file just before trying to
add the node and there still do not seem tb any duplicates.

Could "Error# 35602: Key is not unique in collection" refer to something else
besides the key as in:
 
P

(PeteCresswell)

Per Douglas J. Steele:
Post back if the problem recurs.

I've got a mini-app that provokes the problem - but with a little twist that
makes me think it's at least partially some kind of bad object. i.e. maybe
somebody would slog through the code for a looooong time and never find
anything.

About 400k zipped. You can download it by clicking http://tinyurl.com/nd4rc
 
P

(PeteCresswell)

Per (PeteCresswell):
But it's abending on a different .NodeKey value each time I run it.

Never did figure out the passing of control from the load routine's error trap
to the calling routine's...

But the "real" problem is fixed.

What happened was that Yours Truly loaded a work table ordered by
DealName/TrancheID and then ran a query against said work table with, for
instance, and ORDER BY DealName at the end of it - expecting the records to come
back by DealName/TrancheID because that was the physical sequence of the table.

Seems like that was not the case and maybe even that each time the query is run,
the sequence could be subtly different: still ordered by the specified
"DealName" but differently by the records under a given deal. As a result the
load routine could not reliably tell when a given Tranche began or ended - since
records with a given TrancheID might be in several different groups.

Anyhow... I added ", DealNumber" to every ORDER BY clause and the problem went
away...
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top