Treeview duplicate nodes (VB.NET 2008)

  • Thread starter Thread starter Scott
  • Start date Start date
S

Scott

When populating a Treeview in code, I am getting "duplicate" nodes.
They appear in the treeview, but don't seem to exist when I iterate
through the node collections.

This is what I get:

http://www.emick.us/Capture.JPG

and this is my code. I am baffled.

Scott Emick

Public Class MainForm
Private Sub ProcessButton_Click(ByVal sender As System.Object,
ByVal e As System.EventArgs) Handles ProcessButton.Click
Dim ds1 As New DS
Dim cn As New SqlConnection(My.Settings.WINDY)
cn.Open()
Dim cmd As New SqlCommand
cmd.Connection = cn
cmd.CommandText = "SELECT coalesce([MASTER NUMBER],-1) AS
MASTER_NUMBER FROM SALESTRANSACTIONS WHERE [SOP NUMBER] = '" &
OrderNumberTextBox.Text & "'"
Dim MasterNumber As Integer = cmd.ExecuteScalar
cmd.CommandText = "SELECT [SOP NUMBER], [ORIGINAL NUMBER] FROM
SALESTRANSACTIONS WHERE [MASTER NUMBER] = " & MasterNumber
Dim da As New SqlDataAdapter(cmd)
da.Fill(ds1.OrderTree)
For rc As Integer = 0 To (ds1.OrderTree.Count - 1)
Dim dr As DS.OrderTreeRow = ds1.OrderTree(rc)
If dr.ORIGINAL_NUMBER = "" Then
OrderTreeView.Nodes.Add(dr.SOP_NUMBER)
Console.WriteLine(dr.SOP_NUMBER)
OrderTreeView.ExpandAll()
Application.DoEvents()
Else
'try to find the node to add to
For tc As Integer = 0 To (OrderTreeView.Nodes.Count -
1)
Dim rn As TreeNode = FindNode(dr.ORIGINAL_NUMBER,
OrderTreeView.Nodes(tc))
If Not rn Is Nothing Then
rn.Nodes.Add(dr.SOP_NUMBER)
OrderTreeView.ExpandAll()
Application.DoEvents()
Exit For
End If

Next
End If
Next
End Sub
Private Function FindNode(ByRef Original_Number As String, ByRef
nd As TreeNode) As TreeNode
If nd.Text = Original_Number Then
Return nd
End If
If nd.Nodes.Count > 0 Then
For Each sn As TreeNode In nd.Nodes
Dim rn As TreeNode = FindNode(Original_Number, sn)
If Not rn Is Nothing Then
Return rn
End If
Next
End If
Return Nothing
End Function
 
Funny I changed my arguments to the FindNode function from ByRef to
ByVal and the problems has stopped. Very odd.

Private Function FindNode(ByVal Original_Number As String, ByVal nd As
TreeNode) As TreeNode

When populating a Treeview in code, I am getting "duplicate" nodes.
They appear in the treeview, but don't seem to exist when I iterate
through the node collections.

This is what I get:

http://www.emick.us/Capture.JPG

and this is my code.  I am baffled.

Scott Emick

Public Class MainForm
    Private Sub ProcessButton_Click(ByVal sender As System.Object,
ByVal e As System.EventArgs) Handles ProcessButton.Click
        Dim ds1 As New DS
        Dim cn As New SqlConnection(My.Settings.WINDY)
        cn.Open()
        Dim cmd As New SqlCommand
        cmd.Connection = cn
        cmd.CommandText = "SELECT coalesce([MASTER NUMBER],-1) AS
MASTER_NUMBER FROM SALESTRANSACTIONS WHERE [SOP NUMBER] = '" &
OrderNumberTextBox.Text & "'"
        Dim MasterNumber As Integer = cmd.ExecuteScalar
        cmd.CommandText = "SELECT [SOP NUMBER], [ORIGINAL NUMBER] FROM
SALESTRANSACTIONS WHERE [MASTER NUMBER] = " & MasterNumber
        Dim da As New SqlDataAdapter(cmd)
        da.Fill(ds1.OrderTree)
        For rc As Integer = 0 To (ds1.OrderTree.Count - 1)
            Dim dr As DS.OrderTreeRow = ds1.OrderTree(rc)
            If dr.ORIGINAL_NUMBER = "" Then
                OrderTreeView.Nodes.Add(dr.SOP_NUMBER)
                Console.WriteLine(dr.SOP_NUMBER)
                OrderTreeView.ExpandAll()
                Application.DoEvents()
            Else
                'try to find the node to add to
                For tc As Integer = 0 To (OrderTreeView..Nodes.Count -
1)
                    Dim rn As TreeNode = FindNode(dr.ORIGINAL_NUMBER,
OrderTreeView.Nodes(tc))
                    If Not rn Is Nothing Then
                        rn.Nodes.Add(dr.SOP_NUMBER)
                        OrderTreeView.ExpandAll()
                        Application.DoEvents()
                        Exit For
                    End If

                Next
            End If
        Next
    End Sub
    Private Function FindNode(ByRef Original_Number As String, ByRef
nd As TreeNode) As TreeNode
        If nd.Text = Original_Number Then
            Return nd
        End If
        If nd.Nodes.Count > 0 Then
            For Each sn As TreeNode In nd.Nodes
                Dim rn As TreeNode = FindNode(Original_Number, sn)
                If Not rn Is Nothing Then
                    Return rn
                End If
            Next
        End If
        Return Nothing
    End Function
 
This is expected. The recursive FindNode calls behaves as if you are doing
nd=sn and then OrderTreeView.Nodes(tc)=nd...

Keep in mind that ByRef still passes the original value while ByVal
transmits a copy...

Though it doesn't make a difference when accessing object members (a pointer
(which is basically what an object is) or the copy of a pointer still points
to the same location), changing the object argument itself still does (i.e
you change the pointer itself).

As here you obviously didn't want to change anything, why to use ByRef asa
default ? I would suggest always using ByVal as a default...

I normally do use byval as a default...

Normally the only reasons I use ByRef is either to change something
like you said, or also if the object is very large like a huge dataset
or something it seems like a waste to make a copy of it.

But once in a great while, I get "creative" when doing some ad-hoc
coding....
 
I normally do use byval as a default...

Normally the only reasons I use ByRef is either to change something
like you said, or also if the object is very large like a huge dataset
or something it seems like a waste to make a copy of it.

But once in a great while, I get "creative" when doing some ad-hoc
coding....

And in case anyone else ever has use for some code that displays order
hierarchy in Dynamics GP - Great Plains this is the code that ended up
working. I was just doing for myself, so yes it is sloppy! If I end
up using the logic in one of my projects I will probably create a
class in a library that returns an xml representation of the sop
document heirarchy and then use that for whatever purpose I need it
such as finding the status of individual items from a web order that
has been fulfilled through great plains.

Private Sub ProcessButton_Click(ByVal sender As System.Object,
ByVal e As System.EventArgs) Handles ProcessButton.Click
OrderTreeView.Nodes.Clear()
Dim ds1 As New DS
Dim cn As New SqlConnection(My.Settings.WINDY)
cn.Open()
Dim cmd As New SqlCommand
cmd.Connection = cn
cmd.CommandText = "SELECT coalesce([MASTER NUMBER],-1) AS
MASTER_NUMBER FROM SALESTRANSACTIONS WHERE [SOP NUMBER] = '" &
OrderNumberTextBox.Text & "'"
Dim MasterNumber As Integer = cmd.ExecuteScalar
cmd.CommandText = "SELECT [SOP NUMBER], [ORIGINAL NUMBER],
[sop type] FROM SALESTRANSACTIONS " & _
" WHERE [MASTER NUMBER] = " & MasterNumber & " order by
[original number]"
Dim da As New SqlDataAdapter(cmd)
da.Fill(ds1.OrderTree)
Dim otr As DS.OrderTreeRow() = ds1.OrderTree.Select("[ORIGINAL
NUMBER]=''")
If otr.Length > 0 Then
Dim SOP_NUMBER As String = otr(0).SOP_NUMBER
Dim SOP_TYPE As String = otr(0).sop_type
Dim curnode As TreeNode =
OrderTreeView.Nodes.Add(SOP_NUMBER & " (" & SOP_TYPE & ")")
curnode.Expand()
ListNodeItems(SOP_NUMBER, SOP_TYPE, curnode)
FindChildren(curnode, ds1.OrderTree, SOP_NUMBER)
End If
End Sub
Private Sub FindChildren(ByVal curnode As TreeNode, ByVal ot As
DS.OrderTreeDataTable, ByVal SOP_NUMBER As String)
Dim otr As DS.OrderTreeRow() = ot.Select("[ORIGINAL NUMBER]='"
& SOP_NUMBER & "'")
If otr.Length > 0 Then
For cc As Integer = 0 To (otr.Length - 1)
Dim newnode As TreeNode =
curnode.Nodes.Add(otr(cc).SOP_NUMBER & " (" & otr(cc).SOP_TYPE & ")")
newnode.Expand()
ListNodeItems(otr(cc).SOP_NUMBER, otr(cc).SOP_TYPE,
newnode)
FindChildren(newnode, ot, otr(cc).SOP_NUMBER)
Next
End If
End Sub
Private Sub ListNodeItems(ByVal SOP_NUMBER As String, ByVal
SOP_TYPE As String, ByVal curnode As TreeNode)
Dim gvaOrderTypes As New DS.gvaOrderTypesDataTable
Dim gvaOrderTypesTA As New DSTableAdapters.gvaOrderTypesTA
gvaOrderTypesTA.Fill(gvaOrderTypes)
Dim sli As New DS.SOPLINEITEMSDataTable
Dim sli_table_adapter As New DSTableAdapters.SOPLINEITEMSTA
Dim otr As DS.gvaOrderTypesRow() =
gvaOrderTypes.Select("OrderTypeDesc = '" & SOP_TYPE & "'")
Dim sop_type_key As Integer = otr(0).OrderType
sli_table_adapter.Fill(sli, SOP_NUMBER, sop_type_key)
For Each li As DS.SOPLINEITEMSRow In sli.Rows
Dim itemnmbr As TreeNode = curnode.Nodes.Add(li.ITEMNMBR)
itemnmbr.Nodes.Add("QUANTITY " & li.QUANTITY)
itemnmbr.Nodes.Add("QTYCANCE " & li.QTYCANCE)
itemnmbr.Nodes.Add("QTYFULFI " & li.QTYFULFI)
itemnmbr.Nodes.Add("QTYTBAOR " & li.QTYTBAOR)
itemnmbr.Nodes.Add("QTYTOINV " & li.QTYTOINV)
Next
End Sub
Private Function FindNode(ByVal Original_Number As String, ByVal
nd As TreeNode) As TreeNode
If nd.Text = Original_Number Then
Return nd
End If
If nd.Nodes.Count > 0 Then
For Each sn As TreeNode In nd.Nodes
Dim rn As TreeNode = FindNode(Original_Number, sn)
If Not rn Is Nothing Then
Return rn
End If
Next
End If
Return Nothing
End Function

-- Yes this table should be called SOPTypes, not ordertypes.......This
is the table for the SOPTYPE lookup

CREATE TABLE [dbo].[gvaOrderTypes](
[OrderType] [int] NOT NULL,
[OrderTypeDesc] [varchar](255) NOT NULL,
CONSTRAINT [PK_gvaOrderTypes] PRIMARY KEY CLUSTERED
(
[OrderType] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY
= OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
INSERT [dbo].[gvaOrderTypes] ([OrderType], [OrderTypeDesc]) VALUES (1,
N'Quote')
INSERT [dbo].[gvaOrderTypes] ([OrderType], [OrderTypeDesc]) VALUES (2,
N'Order')
INSERT [dbo].[gvaOrderTypes] ([OrderType], [OrderTypeDesc]) VALUES (3,
N'Invoice')
INSERT [dbo].[gvaOrderTypes] ([OrderType], [OrderTypeDesc]) VALUES (4,
N'Return')
INSERT [dbo].[gvaOrderTypes] ([OrderType], [OrderTypeDesc]) VALUES (5,
N'Back Order')
 
Am 09.04.2010 14:18, schrieb Scott:
I normally do use byval as a default...

Normally the only reasons I use ByRef is either to change something
like you said, or also if the object is very large like a huge dataset
or something it seems like a waste to make a copy of it.

You don't make a copy of a Dataset if you pass it ByVal. You make
a copy of the variable content, and that's just the reference (32/64 bits)
to the DataSet.
 
Normally the only reasons I use ByRef is either to change something
like you said, or also if the object is very large like a huge dataset
or something it seems like a waste to make a copy of it.

Objects are pointers. ByVal, ByRef is just about the pointer, not the data
this pointer points to :

Sub ByValTest(ByVal o As SomeObject)
o.MyProperty="A" ' This change will be seen in the main code (access is
done using a copy of the pointer but it still points to the same location)
o=Nothing ' You set the copy of the pointer itself to 0, this change
won't be seen in the main code as you modify a copy of the pointer value
End Sub

Sub ByValRef(ByRef o As SomeObject)
o.MyProperty="B" ' This change will be seen in the main code (access is
done using the pointer and it still points to the same location)
o=Nothing ' Will set also the argument to nothing as here you are
working on the real value of the pointer, not on a copy...
End Sub

ByVal/ByRef doesn't change the behavior when you modify the object data. It
differs only when you change the object variable itself...
 
And to add to the answer from Armin,

Especially with a dataset you will almost never use the ByRef.

With the dataset it is common practice to use either the Fill or a function
like GetDataset.

The later function creates always a new dataset and returns the reference of
that ByVal, while the Fill fills the dataset which is referenced in the
ByVal.

Cor
 
Back
Top