多窗体跨线程刷新报错,求助Invoke实现方案
解决跨线程刷新多窗体控件的Invoke实现方案
核心原理
Windows窗体控件仅允许由创建它的线程(UI线程)访问,跨线程直接操作控件会触发System.InvalidOperationException。解决方法是通过Control.Invoke方法,将UI操作委托给控件所在的UI线程执行。
修改后的完整代码
Public Shared Sub VerificaAlterações(ByVal MOV_Bancos As List(Of Integer), ByVal MOV_Lançamentos As List(Of Integer), ByVal MOV_Datas As List(Of Date), ByVal DOC_Lojas As List(Of Integer), ByVal DOC_Clientes As List(Of Integer), ByVal DOC_BACs As List(Of String), ByVal DOC_Condições As List(Of Integer), ByVal DOC_Datas As List(Of Date), ByVal DOC_Sacados As List(Of String), ByVal DOCS_Documentos As List(Of String), ByVal DOC_Valores As List(Of Decimal)) Dim n As Integer = 0 For Each FormRefresh As Form In Application.OpenForms() Select Case FormRefresh.Name Case Form1.Name n = 0 For Each BAC In DOC_BACs ' 检查是否需要跨线程调用 If FormRefresh.InvokeRequired Then ' 通过Invoke将操作委托给UI线程执行 FormRefresh.Invoke(Sub() DirectCast(FormRefresh, Form1).Btn1.PerformClick() End Sub) Else DirectCast(FormRefresh, Form1).Btn1.PerformClick() End If n += 1 Next Case Form2.Name n = 0 For Each BAC In DOC_BACs ' 捕获当前循环的n值,避免闭包变量问题 Dim currentN = n If FormRefresh.InvokeRequired Then FormRefresh.Invoke(Sub() Dim form2 = DirectCast(FormRefresh, Form2) If (form2.tbLoja.Text = DOC_Lojas(currentN).ToString OrElse form2.tbLoja.Text = vbNullString OrElse CInt(form2.tbLoja.Text) = 0) AndAlso (form2.tbCliente.Text = DOC_Clientes(currentN).ToString OrElse form2.tbCliente.Text = vbNullString OrElse CInt(form2.tbCliente.Text) = 0) AndAlso (form2._procNome.Text = vbNullString OrElse form2._procNome.Text.Contains(DOC_Sacados(currentN))) AndAlso (form2._procDcto.Text = vbNullString OrElse form2._procNome.Text.Contains(DOCS_Documentos(currentN))) AndAlso (form2._procVlrCond.SelectedIndex = 0) AndAlso (form2._pVenc1.Checked = False OrElse form2._pVenc1.Value <= DOC_Datas(currentN)) AndAlso (form2._pVenc2.Checked = False OrElse form2._pVenc2.Value >= DOC_Datas(currentN)) Then form2.BTN_Pesquisar.PerformClick() Exit For End If End Sub) Else Dim form2 = DirectCast(FormRefresh, Form2) If (form2.tbLoja.Text = DOC_Lojas(currentN).ToString OrElse form2.tbLoja.Text = vbNullString OrElse CInt(form2.tbLoja.Text) = 0) AndAlso (form2.tbCliente.Text = DOC_Clientes(currentN).ToString OrElse form2.tbCliente.Text = vbNullString OrElse CInt(form2.tbCliente.Text) = 0) AndAlso (form2._procNome.Text = vbNullString OrElse form2._procNome.Text.Contains(DOC_Sacados(currentN))) AndAlso (form2._procDcto.Text = vbNullString OrElse form2._procNome.Text.Contains(DOCS_Documentos(currentN))) AndAlso (form2._procVlrCond.SelectedIndex = 0) AndAlso (form2._pVenc1.Checked = False OrElse form2._pVenc1.Value <= DOC_Datas(currentN)) AndAlso (form2._pVenc2.Checked = False OrElse form2._pVenc2.Value >= DOC_Datas(currentN)) Then form2.BTN_Pesquisar.PerformClick() Exit For End If End If n += 1 Next Case Form3.Name n = 0 For Each BAC In DOC_BACs If FormRefresh.InvokeRequired Then FormRefresh.Invoke(Sub() Dim form3 = DirectCast(FormRefresh, Form3) For Each Linha As DataGridViewRow In form3.tabDocumentos.Rows With Linha.DefaultCellStyle .ForeColor = Color.Black .SelectionBackColor = Color.Black End With Next End Sub) Else Dim form3 = DirectCast(FormRefresh, Form3) For Each Linha As DataGridViewRow In form3.tabDocumentos.Rows With Linha.DefaultCellStyle .ForeColor = Color.Black .SelectionBackColor = Color.Black End With Next End If n += 1 Next End Select Next End Sub
关键注意事项
- InvokeRequired判断:每次访问UI控件前必须检查该属性,确保操作在正确的线程执行
- 闭包变量处理:在循环中使用
currentN捕获当前循环的n值,避免Lambda表达式引用同一变量导致的逻辑错误 - 委托简化:使用VB.NET的匿名Sub(Lambda表达式)直接封装UI操作,无需额外定义委托类型
- 窗体类型转换:通过
DirectCast将通用Form对象转换为具体窗体类型,以访问其专属控件和方法
内容的提问来源于stack exchange,提问作者Heitor Badotti
相关产品推荐
相关产品推荐

