如何在BackgroundWorker中调用操作RichTextBox的委托?
问题描述
我创建了一个用于日志记录的委托,RichTextBox控件可见,尝试在BackgroundWorker的DoWork事件中调用该委托但无法正常工作,运行时抛出异常:无效的跨线程操作: 从不是创建控件‘RTB_Log’的线程访问它。
对应的VB.NET代码如下:
Public Class Form1 Public Delegate Sub WriteRtbLogDelegate(ByVal Texto As String, ByVal _Color As Color) Private Sub WriteRTBLog(ByVal Texto As String, ByVal TextColor As Color) If Me.InvokeRequired Then Dim method As WriteRtbLogDelegate = AddressOf Me.WriteRTBLog Me.Invoke(method, Texto, TextColor) Else If RTB_Log.Lines.Length < 1 Then 'ultima = 0 RTB_Log.AppendText("Inicio del LOG - " & Now.ToLongDateString & vbCrLf) End If Dim length As Integer = RTB_Log.Text.Length Texto = Format(Now.Day, "00").ToString & "/" & Format(Now.Month, "00").ToString & "/" & Now.Year.ToString & " - " & Format(Now.Hour, "00").ToString & ":" & Format(Now.Minute, "00").ToString & ":" & Format(Now.Second, "00").ToString & " " & Texto RTB_Log.AppendText(Texto + vbCrLf) RTB_Log.SelectionStart = length RTB_Log.SelectionLength = Texto.Length RTB_Log.SelectionColor = TextColor ' The following expression was wrapped in a checked-expression RTB_Log.[Select](RTB_Log.SelectionStart + RTB_Log.SelectionLength, 0) RTB_Log.SelectionColor = TextColor RTB_Log.SelectionLength = 0 RTB_Log.SelectionStart = RTB_Log.TextLength RTB_Log.ScrollToCaret() If RTB_Log.TextLength > 1000000.0 Then With RTB_Log .SaveFile(Application.StartupPath & "\" & Format(Now.Day, "00").ToString & "-" & Format(Now.Month, "00").ToString & "-" & Now.Year.ToString & " - " & Format(Now.Hour, "00").ToString & Format(Now.Minute, "00").ToString & Format(Now.Second, "00").ToString & ".log", System.Windows.Forms.RichTextBoxStreamType.RichText) .Clear() .Text = "Log guardado en " & Application.StartupPath & Now.ToShortDateString & ".log" End With End If End If End Sub End Class Private Sub BGW1_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs) Handles BGW1.DoWork WriteRTBLog("Starting...", Color.Blue) End Sub
解决方案
问题根源在于:代码中判断Me.InvokeRequired(Me指Form1),但跨线程访问的目标是RTB_Log控件。当BackgroundWorker的DoWork线程调用WriteRTBLog时,Me.InvokeRequired可能返回False(比如Form句柄未完全创建时),导致直接执行了访问RTB_Log的代码,触发跨线程异常。
以下两种修改方案均可解决问题:
方案一:针对RTB_Log控件判断InvokeRequired
将线程检查的对象从Form改为RTB_Log控件,确保针对目标控件做线程同步判断:
Private Sub WriteRTBLog(ByVal Texto As String, ByVal TextColor As Color) ' 改为检查RTB_Log的InvokeRequired If RTB_Log.InvokeRequired Then Dim method As WriteRtbLogDelegate = AddressOf Me.WriteRTBLog ' 使用RTB_Log的Invoke方法 RTB_Log.Invoke(method, Texto, TextColor) Else ' 原有日志写入逻辑保持不变 If RTB_Log.Lines.Length < 1 Then RTB_Log.AppendText("Inicio del LOG - " & Now.ToLongDateString & vbCrLf) End If Dim length As Integer = RTB_Log.Text.Length Texto = Format(Now.Day, "00").ToString & "/" & Format(Now.Month, "00").ToString & "/" & Now.Year.ToString & " - " & Format(Now.Hour, "00").ToString & ":" & Format(Now.Minute, "00").ToString & ":" & Format(Now.Second, "00").ToString & " " & Texto RTB_Log.AppendText(Texto + vbCrLf) RTB_Log.SelectionStart = length RTB_Log.SelectionLength = Texto.Length RTB_Log.SelectionColor = TextColor RTB_Log.[Select](RTB_Log.SelectionStart + RTB_Log.SelectionLength, 0) RTB_Log.SelectionColor = TextColor RTB_Log.SelectionLength = 0 RTB_Log.SelectionStart = RTB_Log.TextLength RTB_Log.ScrollToCaret() If RTB_Log.TextLength > 1000000 Then With RTB_Log .SaveFile(Application.StartupPath & "\" & Format(Now.Day, "00").ToString & "-" & Format(Now.Month, "00").ToString & "-" & Now.Year.ToString & " - " & Format(Now.Hour, "00").ToString & Format(Now.Minute, "00").ToString & Format(Now.Second, "00").ToString & ".log", System.Windows.Forms.RichTextBoxStreamType.RichText) .Clear() .Text = "Log guardado en " & Application.StartupPath & Now.ToShortDateString & ".log" End With End If End If End Sub
方案二:使用BackgroundWorker的ProgressChanged事件(更符合BGW设计)
BackgroundWorker内置了ProgressChanged事件,该事件自动在UI线程执行,无需手动处理Invoke:
- 启用BGW的
WorkerReportsProgress属性(可在Form加载或BGW初始化时设置):
BGW1.WorkerReportsProgress = True
- 订阅
ProgressChanged事件,在事件处理方法中写入日志:
Private Sub BGW1_ProgressChanged(sender As Object, e As ProgressChangedEventArgs) Handles BGW1.ProgressChanged ' 通过UserState传递日志文本和颜色,这里用Tuple封装数据 Dim logTuple = DirectCast(e.UserState, Tuple(Of String, Color)) WriteRTBLog(logTuple.Item1, logTuple.Item2) End Sub
- 在DoWork事件中调用
ReportProgress触发UI更新:
Private Sub BGW1_DoWork(sender As Object, e As DoWorkEventArgs) Handles BGW1.DoWork ' 进度值可随意填写(如0),通过UserState传递日志数据 BGW1.ReportProgress(0, Tuple.Create("Starting...", Color.Blue)) End Sub
这种方式贴合BackgroundWorker的设计模式,避免手动处理线程同步的潜在问题。
内容的提问来源于stack exchange,提问作者E_Blue
相关产品推荐
相关产品推荐

