【问题标题】:Handle value change of control inside UserControl inside FlowLayoutPanel处理 FlowLayoutPanel 内 UserControl 内控件的值更改
【发布时间】:2012-05-10 14:49:14
【问题描述】:

我正在制作发票申请。我在 FlowLayoutPanel (pnlEntries) 内的 UserControl (InvoiceEntry) 内有一个标签 (lblCost)。 InvoiceEntry 代表发票上的一个行项目,pnlEntries 是发票的“正文”。 pnlEntries 可以包含多个 InvoiceEntry 控件。

我正在尝试对每个 InvoiceEntry 控件中的所有 lblCost 值求和以进行小计,并且我希望该小计在成本发生变化时自动更改(例如,订购数量的变化)。当 pnlEntries 中包含的任何 InvoiceEntry 控件的 lblCost.Text 属性发生变化时,是否有办法处理?

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

【问题讨论】:

    标签: vb.net user-controls event-handling controls flowlayoutpanel


    【解决方案1】:

    由于您已经在 InvoiceEntry 中获得了 TextChanged 事件处理程序,因此您可以轻松调用 InvoiceAdd Form 中的 UpdateTotal 函数

    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
    

    【讨论】:

    • 不幸的是,没有。标签在 UserControl 中生成,并通过将在数据库中找到的项目价格乘以数量来为其分配一个值。我通过让 UserControl 执行 Parent.Focus(),然后在 pnlResults.GotFocus() 的表单中添加一个处理程序来解决这个问题,但我很确定这更像是一种 hack,而不是合法代码。
    • 所以你无法触摸 UserControl 源代码,只能在 Parent 对象上工作?
    • 如果是这种情况,我建议您为父对象添加一个 KeysUp 事件处理程序并检查 lblCost 的文本是否更改
    • 我可以触摸 UserControl 源代码。问题是,如果我有 40 个行项目,我不想在主窗体上为每个人的 TextChanged 事件编写代码 - 并且 UserControl 无权访问主窗体上的小计标签,所以我也不能把代码放在那里。而且,KeyUp 将再次不起作用,因为我必须为每个控件编写一个 KeyUp,并且由于它们是在运行时动态添加的,我认为这是不可能的,因为我不知道将添加多少 UserControls .
    • 如果您有 40 个行项目,则不需要编写 40 个不同的 TextChanged 事件,而只需编写一个,因此您可以将该单个事件分配给所有动态控件。您还可以在主窗体中创建一个属性,该属性可以从您的单个 TextChanged 事件中更新总值。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-11-18
    • 1970-01-01
    • 1970-01-01
    • 2017-12-16
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多