调用 ListView 运行 sub 两次

Invoke of ListView runs sub twice

我已经运行使用以下代码好几天了,但是在昨天进行了一些调试之后,我发现 Sub 已经触发了两次。一些背景 - 这是一个邮寄应用程序,可以运送我们所有的包裹。我需要在主窗体中添加一个列表视图,以显示从 Easypost 返回的各种费率。当我第一次添加列表视图时,应用程序 运行 从调试中很好,但在编译后它会在填充列表视图后完全挂起。因此,经过几天的搜索后,我添加了调用方法,该方法允许应用程序 GUI 在填充列表视图后继续。因此,首先我查看是否返回费率,并查看当它到达 lvRates.InvokeRequired 时它会调用,然后第二次执行 EasyPostGetServices,这会浪费资源并在 easypost 加载返回的第二次费率时创建到货件。

Private Sub GetEasyPostServices()


    EasyPostGetServices(dictEPFromAddress, dictEPToAddress, dictEPPackage)

    If EasyPostShipment.rates.Count > 0 Then
        If lvRates.InvokeRequired Then
            lvRates.Invoke(New MethodInvoker(AddressOf GetEasyPostServices))
        Else
            For Each rate As Rate In EasyPostShipment.rates
                lvRates.Items.Add(New ListViewItem({rate.carrier, rate.rate, UCase(rate.service), rate.id}))
            Next
        End If

    Else
        MsgBox("Based on your inputs or preferences, no shipping services were available. Shipping services shown ignore your preferences. If none are shown, there are no shipping services available for this order.")
    End If
End Sub

我的问题是为什么代码 运行ning 两次,我该如何防止它发生。我已经移动了调用,但是当 运行 从 exe.

时,我尝试 GUI 的所有其他东西都冻结了

这是基于您最初的问题。我在单击按钮时添加了一个 BackgroundWorker,使 GetEasyPostServices 在非 UI 线程上启动 运行ning。这里的关键是如何正确调用 UI 调用。底部的三个方法证明了这一点。

Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    ' this method is running on the UI thread as it handles a UI event
    Dim bw As New System.ComponentModel.BackgroundWorker()
    AddHandler bw.DoWork, New System.ComponentModel.DoWorkEventHandler(Sub(oo, ee) GetEasyPostServices())
    ' this call causes GetEasyPostServices to run on a non-UI thread
    bw.RunWorkerAsync()
End Sub

Private Sub GetEasyPostServices()
    ' running on a non-UI thread. Any UI calls must be invoked
    Cursor.Current = Cursors.WaitCursor
    Try
        Dim frAddress As New Dictionary(Of String, Object)() From {
            {"street1", "12345 Hill Dr"},
            {"street2", ""},
            {"city", "sometown"}}

        Dim toAddress As New Dictionary(Of String, Object)() From {
            {"street1", sAdd1},
            {"city", sCity},
            {"state", sRegion},
            {"verifications", New List(Of String)() From {"delivery"}}}

        Dim package As New Dictionary(Of String, Object)
        If GlobalVariables.giLength > 0 Then
            package.Add("length", GlobalVariables.giLength.ToString)
            package.Add("width", GlobalVariables.giWidth.ToString)
            package.Add("height", GlobalVariables.giHeight.ToString)
            package.Add("weight", (sLbs * 16) + sOz)
        End If

        EasyPostGetServices(frAddress, toAddress, package)

        If EasyPostShipment.rates.Count > 0 Then
            ' hide the UI invocation logic in methods
            updateListView()
        Else
            MsgBox("Based on your inputs or preferences, no shipping services were available. Shipping services shown ignore your preferences. If none are shown, there are no shipping services available for this order.")
            clearAndFocusTbOrder()
        End If
    Finally
        Cursor.Current = Cursors.Default
    End Try
End Sub

' these methods manage their own UI invocation.

Private Sub updateListView()
    If lvRates.InvokeRequired Then
        lvRates.Invoke(New Action(AddressOf updateListView))
    Else
        lvRates.Visible = True
        lvRates.Items.Clear()
        For Each rate As Rate In EasyPostShipment.rates
            lvRates.Items.Add(New ListViewItem({rate.carrier, rate.rate, UCase(rate.service), rate.id}))
        Next
        SortListView()
        lvRates.Items(0).Selected = True
        Button1.Visible = True
    End If
End Sub

Private Sub SortListView()
    If lvRates.InvokeRequired Then
        lvRates.Invoke(New Action(AddressOf SortListView))
    Else
        lvRates.Sort()
    End If
End Sub

Private Sub clearAndFocusTbOrder()
    If tbOrderID.InvokeRequired Then
        tbOrderID.Invoke(New Action(AddressOf clearAndFocusTbOrder))
    Else
        tbOrderID.Clear()
        tbOrderID.Focus()
    End If
End Sub

一切都是关于您调用什么、在哪里以及为什么调用。在您的示例中,不清楚 GetEasyPostServices 是否在 UI 上启动。我没有这样做,所以我可以演示如何正确调用 UI 调用。然后您需要调用 UI 调用,因为它当前不在 UI 线程上。那就是 "why".

此外,运行 在 UI 线程上使用最少的代码以防止不必要的处理和挂起 UI 线程是关键。您的原始配置 运行 所有代码一次,然后 运行 所有 再次 ,在 UI 线程上。