0

I'd be grateful if anybody could shed some light on a problem. I have a form showing a sales order, this has a DGV (DGVDocs) showing a list of invoices against that order. I've populated a print button with the documents and for each a submenu with Print, Preview, PDF. The lambda expressions on the submenu always pick up the last entry on the menu.

Private Sub create_print_menu()
    Dim i As Integer = 0

    ms = New ContextMenuStrip

    If dgvDocs.Rows.Count > 0 Then
        Dim doctype As String = ""
        Dim docno As Integer = 0

        For i = 0 To dgvDocs.Rows.Count - 1
            ms.Items.Add(RTrim(dgvDocs.Rows(i).Cells(0).Value) & " " & RTrim(dgvDocs.Rows(i).Cells(2).Value))
            jc = ms.Items(ms.Items.Count - 1)
            doctype = RTrim(dgvDocs.Rows(i).Cells(0).Value)
            docno = RTrim(dgvDocs.Rows(i).Cells(2).Value)
            jc.DropDownItems.Add("Preview", Nothing, Function(sender, e) docPreview(doctype, docno))


        Next
    End If

End Sub

Private Function docPreview(ByVal doctype As String, ByVal docno As Integer)
    If doctype.ToUpper.Contains("DESPATCH NOTE") Then
        Dim frm As New frmDespatchPreview
        frm.delnote = docno
        frm.ShowDialog()
    ElseIf doctype.ToUpper.Contains("INVOICE") Then
        Dim frm As New frmInvoicePreview
        frm.invno = docno
        frm.ShowDialog()

    End If

    Return True

    Return True

End Function
Graham
  • 57
  • 1
  • 11
  • I don't see any lambda expressions. – Derek Aug 07 '15 at 12:29
  • possible duplicate of [Why is it bad to use an iteration variable in a lambda expression](http://stackoverflow.com/questions/227820/why-is-it-bad-to-use-an-iteration-variable-in-a-lambda-expression) – Heinzi Aug 07 '15 at 13:07
  • @Derek: `jc.DropDownItems.Add("Preview", Nothing, Function(sender, e) docPreview(doctype, docno))` – Heinzi Aug 07 '15 at 13:07

1 Answers1

0

when you pass the lambda in your loop:

Function(sender, e) docPreview(doctype, docno)

... you are not passing in a copy or snapshot of whatever value is in doctype and docno at the time of that specific loop iteration. You are actually passing in a reference to those variables.

So, by the end of the loop, all the lambdas will effectively have a reference to whatever the last value of doctype and docno is.

To avoid this problem, make sure each lambda refers to a different variable reference. This can be accomplished by declaring doctype and docno inside the loop, like this:

'Dim doctype As String = ""
'Dim docno As Integer = 0

For i = 0 To dgvDocs.Rows.Count - 1
    ms.Items.Add(RTrim(dgvDocs.Rows(i).Cells(0).Value) & " " & RTrim(dgvDocs.Rows(i).Cells(2).Value))
    jc = ms.Items(ms.Items.Count - 1)
    Dim doctype As String = RTrim(dgvDocs.Rows(i).Cells(0).Value)
    Dim docno As Integer = RTrim(dgvDocs.Rows(i).Cells(2).Value)
    jc.DropDownItems.Add("Preview", Nothing, Function(sender, e) docPreview(doctype, docno))
Next

EDIT:

Relevant article: Closures in VB Part 5: Looping

sstan
  • 35,425
  • 6
  • 48
  • 66