调用 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 线程上。
我已经运行使用以下代码好几天了,但是在昨天进行了一些调试之后,我发现 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 线程上。