You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

BackgroundWorker事件访问ObjectListView引发跨线程错误求助

解决VB.NET WinForms中BackgroundWorker更新ObjectListView的两个问题

一、解决“跨线程操作无效”错误

出现这个问题的核心原因是BackgroundWorker的Completed事件没有在UI线程触发,大概率是你用了System.Timers.Timer(其Elapsed事件在后台线程),导致BackgroundWorker在后台线程被创建,它的Completed事件会绑定到后台线程的同步上下文,而非UI线程。解决方法:

  • 替换Timer类型:把System.Timers.Timer换成System.Windows.Forms.Timer,它的Tick事件天然在UI线程执行,此时创建并启动的BackgroundWorker,其Completed事件会自动回到UI线程,直接访问ObjectListView就不会触发跨线程错误。
  • 手动指定同步上下文(如果必须用System.Timers.Timer):
    1. 在窗体构造函数中保存UI线程的同步上下文:
      Private uiSyncContext As SynchronizationContext
      Public Sub New()
          InitializeComponent()
          uiSyncContext = SynchronizationContext.Current
      End Sub
      
    2. 在Timer的Elapsed事件中创建BackgroundWorker时,指定其同步上下文:
      Private Sub Timer_Elapsed(sender As Object, e As ElapsedEventArgs)
          Dim bgWorker As New BackgroundWorker()
          bgWorker.SynchronizationContext = uiSyncContext
          ' 绑定DoWork、RunWorkerCompleted事件
          AddHandler bgWorker.DoWork, AddressOf BgWorker_DoWork
          AddHandler bgWorker.RunWorkerCompleted, AddressOf BgWorker_RunWorkerCompleted
          bgWorker.RunWorkerAsync()
      End Sub
      
  • 兜底方案:强制在UI线程操作:如果不确定当前线程是否为UI线程,用ObjectListView的Invoke方法包裹操作逻辑:
    Private Sub BgWorker_RunWorkerCompleted(sender As Object, e As RunWorkerCompletedEventArgs)
        ObjectListView1.Invoke(Sub()
                                    ' 这里安全访问SelectedItems或执行UI操作
                                    Dim selectedItems = ObjectListView1.SelectedItems
                                    ' 恢复选中项的逻辑
                                End Sub)
    End Sub
    

二、解决SetObjects丢失选中项的问题

ObjectListView 2.91的SetObjects默认会保留选中项,但前提是它能识别新旧对象是同一个。如果丢失,按以下步骤排查:

  • 给ObjectListView设置PrimaryKey:在设计器或代码中指定对象的唯一标识属性(比如ID),这样OLV能通过主键匹配对象:
    ObjectListView1.PrimaryKey = "Id" ' 假设你的数据对象有Id属性作为唯一标识
    
  • 重写数据对象的Equals和GetHashCode:如果没设置PrimaryKey,确保你的数据对象重写这两个方法,让OLV能判断对象是否相同:
    Public Class YourDataObject
        Public Property Id As Integer
        Public Property Name As String
    
        Public Overrides Function Equals(obj As Object) As Boolean
            Return obj IsNot Nothing AndAlso TypeOf obj Is YourDataObject AndAlso DirectCast(obj, YourDataObject).Id = Me.Id
        End Function
    
        Public Overrides Function GetHashCode() As Integer
            Return Id.GetHashCode()
        End Function
    End Class
    
  • 手动保存并恢复选中项:如果以上方法无效,在更新前手动保存选中项的唯一标识,更新后重新选中:
    1. 在启动BackgroundWorker前(UI线程)保存选中项的ID:
      Private selectedIds As List(Of Integer)
      Private Sub StartUpdate()
          selectedIds = ObjectListView1.SelectedItems.Cast(Of YourDataObject)().Select(Function(x) x.Id).ToList()
          ' 启动BackgroundWorker
          bgWorker.RunWorkerAsync()
      End Sub
      
    2. 在Completed事件中(确保在UI线程)恢复选中:
      Private Sub BgWorker_RunWorkerCompleted(sender As Object, e As RunWorkerCompletedEventArgs)
          Dim newData = DirectCast(e.Result, List(Of YourDataObject))
          ObjectListView1.SetObjects(newData)
          ' 恢复选中项
          For Each id In selectedIds
              Dim item = newData.FirstOrDefault(Function(x) x.Id = id)
              If item IsNot Nothing Then
                  ObjectListView1.SelectObject(item)
              End If
          Next
      End Sub
      

内容的提问来源于stack exchange,提问作者johnfermor

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.04 04:10:27