Handle value change of control inside UserControl inside FlowLayoutPanel
-
04-06-2021 - |
質問
I am making an invoicing application. I have a label (lblCost) inside of a UserControl (InvoiceEntry) inside of a FlowLayoutPanel (pnlEntries). InvoiceEntry represents a line item on the invoice, and pnlEntries is the "body" of the invoice. pnlEntries can hold several InvoiceEntry controls.
I am attempting to sum all of the lblCost values from each InvoiceEntry control to make a subtotal, and I want that subtotal to change automatically if costs are changed (e.g., a change in quantities being ordered). Is there a way to handle when the lblCost.Text property of any of the InvoiceEntry controls contained within pnlEntries changes?
InvoiceAdd.vb:
' Instantiate objects of the various database interaction classes.
Private mCustomer As New Customers
Private mItem As New Items
Private mInvoices As New Invoices
Private mInvoiceItem As New InvoiceItems
' An array of InvoiceEntries for DB processing
Private Entries() As InvoiceEntry
Private numEntries As Integer = 0
Private Sub InvoiceAdd_Load(sender As System.Object, e As System.EventArgs) _
Handles MyBase.Load
Me.CustomersTableAdapter.Fill(Me.Bauer_BusinessDataSet.Customers)
' Set the DataSource properties of the invEntry control.
' NOTE: For some reason, if this is done inside the
' control code, the program attempts to look in the
' wrong directory for the database. I'm not entirely
' sure why this is. Setting these properties in the form code,
' rather than in the control code, is a successful workaround.
With invEntry.cboItem
.DataSource = mItem.Items
.DisplayMember = "ItemName"
.ValueMember = "Id"
End With
End Sub
Private Sub UpdateTotal() Handles nudTaxRate.TextChanged, _
pnlEntries.GotFocus
Dim total As Decimal = 0
If Entries IsNot Nothing Then
For Each x In Entries
total += CDec(x.lblTotal.Text)
Next
lblSubtotal.Text = total.ToString("C")
lblTax.Text = (lblSubtotal.Text * (nudTaxRate.Text / 100)).ToString("C")
Dim subtotal As Decimal = 0
Dim tax As Decimal = 0
If Not lblSubtotal.Text = Nothing And Not lblTax.Text = Nothing Then
Decimal.TryParse(lblSubtotal.Text.Substring(1), subtotal)
Decimal.TryParse(lblTax.Text.Substring(1), tax)
End If
lblGrandTotal.Text = (subtotal + tax).ToString("C")
End If
End Sub
Public Sub invEntry_ItemSelected() Handles invEntry.ItemSelected
' Increment the number of entries to reflect the addition of a new entry.
numEntries += 1
' ReDim the Entries array to compensate for a new item being added.
ReDim Preserve Entries(numEntries - 1)
' Store the line item that was selected in the Entries array.
Entries(numEntries - 1) = invEntry
' Set the selected line item to a new blank line item and
' add it to the pnlEntries' Controls Collection.
invEntry = New InvoiceEntry
With invEntry
.Name = "Textbox" & numEntries - 1
.Location = New Point(10, (numEntries - 1) * (.Height + 30))
With .cboItem
.DataSource = mItem.Items
.DisplayMember = "ItemName"
.ValueMember = "Id"
End With
End With
pnlEntries.Controls.Add(invEntry)
' Enable the remove button on the previous list item, as
' it is no longer an empty entry.
Entries(numEntries - 1).btnRemove.Enabled = True
End Sub
Public Sub pnlEntries_ControlRemoved() Handles pnlEntries.ControlRemoved
numEntries -= 1
ReDim Preserve Entries(numEntries - 1)
' As the Entries array does not know which control was removed,
' repopulate it with the controls from the panel.
' NOTE: This intentionally leaves the blank line item (invEntry)
' out of the array, as this array will be used to add
' the invoice line items to the database.
For x As Integer = 0 To Entries.Count - 1
Entries(x) = pnlEntries.Controls(x)
Next
End Sub
InvoiceEntry.vb:
' Instantiate an object to interact with the Item table.
Private mItem As New Items
Public Event ItemSelected As EventHandler
Private Sub cboItem_SelectionChangeCommitted(sender As System.Object, _
e As System.EventArgs) _
Handles cboItem.SelectionChangeCommitted
RaiseEvent ItemSelected(Me, EventArgs.Empty)
End Sub
Private Sub InvoiceEntry_Load(sender As System.Object, _
e As System.EventArgs) _
Handles MyBase.Load
' Set the various properties of the Item combobox.
With cboItem
.SelectedIndex = -1
.DropDownStyle = ComboBoxStyle.DropDownList
End With
' Set the Remove button to be disabled by default.
btnRemove.Enabled = False
End Sub
Private Sub cboItem_SelectedIndexChanged(sender As System.Object, _
e As System.EventArgs) _
Handles cboItem.SelectedIndexChanged
' If nothing is selected, clear and disable all relevant controls.
If cboItem.SelectedIndex = -1 Or cboItem.Text = "" Then
lblDescription.Text = ""
lblTotal.Text = ""
nudQuantity.Enabled = False
lblPrice.Text = ""
Else
' Else, set the control texts to their respective values.
lblDescription.Text = _
cboItem.DataSource.Rows(cboItem.SelectedIndex)("Description")
lblPrice.Text = cboItem.DataSource.Rows(cboItem.SelectedIndex)("Price")
lblTotal.Text = (lblPrice.Text * nudQuantity.Value).ToString("C")
nudQuantity.Enabled = True
End If
End Sub
Private Sub lblTotal_TextChanged(sender As System.Object, _
e As System.EventArgs) _
Handles lblTotal.TextChanged
' This is part of my workaround that I describe in the comments section
' of this StackOverflow question.
Parent.Focus()
End Sub
Private Sub nudQuantity_ValueChanged(sender As System.Object, _
e As System.EventArgs) _
Handles nudQuantity.TextChanged
' If the quantity changes, set the total price to reflect this.
lblTotal.Text = (lblPrice.Text * nudQuantity.Value).ToString("C")
End Sub
Private Sub btnRemove_Click(sender As System.Object, _
e As System.EventArgs) _
Handles btnRemove.Click
' Remove the control from the parent design.
Me.Parent.Controls.Remove(Me)
End Sub
解決
Since you have already got the TextChanged event handler within InvoiceEntry, you can easily call the UpdateTotal function in InvoiceAdd Form
InvoiceEntry.vb
Private Sub lblTotal_TextChanged(sender As System.Object, _
e As System.EventArgs) _
Handles lblTotal.TextChanged
' This is part of my workaround that I describe in the comments section
' of this StackOverflow question.
'Parent.Focus()
'Me.Parent could give you the parent control which is pnlEntry not InvocieAdd form
'you need to use FindForm here
Dim MyParentForm As InvoiceAdd = CType(Me.FindForm(), InvoiceAdd)
MyParentForm.UpdateTotal()
End Sub
InvoiceAdd.vb
Public Sub UpdateTotal() Handles nudTaxRate.TextChanged, _
pnlEntries.GotFocus
'Change the function to Public
Dim total As Decimal = 0
If Entries IsNot Nothing Then
For Each x In Entries
total += CDec(x.lblTotal.Text)
Next
lblSubtotal.Text = total.ToString("C")
lblTax.Text = (lblSubtotal.Text * (nudTaxRate.Text / 100)).ToString("C")
Dim subtotal As Decimal = 0
Dim tax As Decimal = 0
If Not lblSubtotal.Text = Nothing And Not lblTax.Text = Nothing Then
Decimal.TryParse(lblSubtotal.Text.Substring(1), subtotal)
Decimal.TryParse(lblTax.Text.Substring(1), tax)
End If
lblGrandTotal.Text = (subtotal + tax).ToString("C")
End If
End Sub