vb.net 在 Visual Basic (VS 2012 V11) 中进行跨线程时正确更新文本框

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/16529092/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-09 16:53:35  来源:igfitidea点击:

Update Text Box Properly when Cross-threading in Visual Basic (VS 2012 V11)

vb.netmultithreadingtextboxbackgroundworker

提问by user2348797

  • Here is my question in short: How do I use the BackGroundWorker (or InvokeRequired method) to make thread-safe calls to append text to a text box?

  • Here is my question in with much detail and background: I've been working on a program that copies file from one location to another for backup purposes. I set an option that will save a file when the file is modified using the FileSysteWatcher. Here is the code:

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Me.Load
    
        Dim directoryPath As String = Path.GetDirectoryName(TextBox1.Text)
        Dim varFileSystemWatcher As New FileSystemWatcher()
        varFileSystemWatcher.Path = directoryPath
    
        varFileSystemWatcher.NotifyFilter = (NotifyFilters.LastWrite)
        varFileSystemWatcher.Filter = Path.GetFileName(TextBox1.Text)
        AddHandler varFileSystemWatcher.Changed, AddressOf OnChanged
        varFileSystemWatcher.EnableRaisingEvents = True
    
    End Sub
    
    Private Sub OnChanged(source As Object, ByVal e As FileSystemEventArgs)
    
        My.Computer.FileSystem.CopyFile(e.FullPath, TextBox2.Text & "\" & e.Name, True)
        TextBox3.ApendText("[New Text]") ' This line causes an error
    End Sub
    
  • 简而言之,这是我的问题:如何使用 BackGroundWorker(或 InvokeRequired 方法)进行线程安全调用以将文本附加到文本框?

  • 这是我的问题,有很多细节和背景:我一直在研究一个程序,该程序将文件从一个位置复制到另一个位置以进行备份。我设置了一个选项,当使用 FileSysteWatcher 修改文件时,该选项将保存文件。这是代码:

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Me.Load
    
        Dim directoryPath As String = Path.GetDirectoryName(TextBox1.Text)
        Dim varFileSystemWatcher As New FileSystemWatcher()
        varFileSystemWatcher.Path = directoryPath
    
        varFileSystemWatcher.NotifyFilter = (NotifyFilters.LastWrite)
        varFileSystemWatcher.Filter = Path.GetFileName(TextBox1.Text)
        AddHandler varFileSystemWatcher.Changed, AddressOf OnChanged
        varFileSystemWatcher.EnableRaisingEvents = True
    
    End Sub
    
    Private Sub OnChanged(source As Object, ByVal e As FileSystemEventArgs)
    
        My.Computer.FileSystem.CopyFile(e.FullPath, TextBox2.Text & "\" & e.Name, True)
        TextBox3.ApendText("[New Text]") ' This line causes an error
    End Sub
    

The code works fine except for updating the text in textbox3. I need the textbox to update when the file specified is modified. This is important so that the user can know the program is working and to have a complete log of the programs operations.

除了更新 textbox3 中的文本外,代码工作正常。我需要在修改指定的文件时更新文本框。这很重要,以便用户可以知道程序正在运行并拥有程序操作的完整日志。

The error that is caused is:

导致的错误是:

Cross-thread operation not valid: Control 'TextBox3' accessed from a thread other than the thread it was created on.

跨线程操作无效:控制“TextBox3”从创建它的线程以外的线程访问。

I'm assuming "AddHandler varFileSystemWatcher.Changed, AddressOf OnChanged" creates a new thread. Also, updating "textbox3" within the "OnChange Sub" (in the way I did) is not a thread-safe manner. So I did some research and found this article:

我假设“AddHandler varFileSystemWatcher.Changed, AddressOf OnChanged”创建了一个新线程。此外,在“OnChange Sub”中更新“textbox3”(以我的方式)不是线程安全的方式。所以我做了一些研究,发现了这篇文章:

How to: Make Thread-Safe Calls to Windows Forms Controls

如何:对 Windows 窗体控件进行线程安全调用

The article explains that one can use InvokeRequired or a BackgroundWorker to make thread-safe calls. I would like to use the BackgroundWorker to make a thread-safe call (if InvokeRequired is more efficient then I'll use that), but this portion, of the example provied, leaves me confused:

文章解释了可以使用 InvokeRequired 或 BackgroundWorker 进行线程安全调用。我想使用 BackgroundWorker 进行线程安全调用(如果 InvokeRequired 更有效,那么我将使用它),但是提供的示例的这一部分让我感到困惑:

' Do I need these imports to use the BackgroundWorker or InvokeRequired?
Imports System
Imports System.ComponentModel
Imports System.Threading
Imports System.Windows.Forms

Public Class Form1
   Inherits Form ' Do I need this for what I am trying to do?

   ' This delegate enables asynchronous calls for setting
   ' the text property on a TextBox control.
   Delegate Sub SetTextCallback([text] As String)

   ' This thread is used to demonstrate both thread-safe and
   ' unsafe ways to call a Windows Forms control.
   Private demoThread As Thread = Nothing

   ' This BackgroundWorker is used to demonstrate the 
   ' preferred way of performing asynchronous operations.
   Private WithEvents backgroundWorker1 As BackgroundWorker

   Private textBox1 As TextBox
   Private WithEvents setTextUnsafeBtn As Button
   Private WithEvents setTextSafeBtn As Button
   Private WithEvents setTextBackgroundWorkerBtn As Button

   ' What is this part of the code for and do I need it?
   Private components As System.ComponentModel.IContainer = Nothing

   ' Again, What is this part of the code for and do I need it?
   Public Sub New()
      InitializeComponent()
    End Sub

   ' And again, What is this part of the code for and do I need it?
   Protected Overrides Sub Dispose(disposing As Boolean)
      If disposing AndAlso (components IsNot Nothing) Then
         components.Dispose()
      End If
      MyBase.Dispose(disposing)
    End Sub

Many parts of the above code leave me confused. What does it do? What parts of this code do I need to use the BackgroundWorker? What parts for the InvokeRequired method? Again, How do I use the BackGroundWorker (or InvokeRequired method) to make thread-safe calls to append text to a text box?(It'd be great to have the code above explained, but all I really need is one example of how to update the text of a text box in a thread-safe manner.)

上面代码的许多部分让我感到困惑。它有什么作用?使用 BackgroundWorker 需要这段代码的哪些部分?InvokeRequired 方法的哪些部分?同样,如何使用 BackGroundWorker(或 InvokeRequired 方法)进行线程安全调用以将文本附加到文本框?(解释上面的代码会很棒,但我真正需要的只是一个示例,说明如何以线程安全的方式更新文本框的文本。)

回答by Idle_Mind

Change:

改变:

Private Sub OnChanged(source As Object, ByVal e As FileSystemEventArgs)
    My.Computer.FileSystem.CopyFile(e.FullPath, TextBox2.Text & "\" & e.Name, True)
    TextBox3.ApendText("[New Text]") ' This line causes an error
End Sub

To:

到:

Private Sub OnChanged(source As Object, ByVal e As FileSystemEventArgs)
    My.Computer.FileSystem.CopyFile(e.FullPath, TextBox2.Text & "\" & e.Name, True)
    AppendTextBox(TextBox3, "[New Text]")
End Sub

Private Delegate Sub AppendTextBoxDelegate(ByVal TB As TextBox, ByVal txt As String)

Private Sub AppendTextBox(ByVal TB As TextBox, ByVal txt As String)
    If TB.InvokeRequired Then
        TB.Invoke(New AppendTextBoxDelegate(AddressOf AppendTextBox), New Object() {TB, txt})
    Else
        TB.AppendText(txt)
    End If
End Sub

回答by Eivind Gussi?s L?kseth

You can also use SynchronizationContext. Be careful to get a reference to it from the constructor. There's a great article on this at CodeProject.com: http://www.codeproject.com/Articles/14265/The-NET-Framework-s-New-SynchronizationContext-Cla

您还可以使用 SynchronizationContext。小心从构造函数中获取对它的引用。CodeProject.com 上有一篇很棒的文章:http: //www.codeproject.com/Articles/14265/The-NET-Framework-s-New-SynchronizationContext-Cla

Imports System.IO
Imports System.Threading

Public Class Form1
    Private m_SyncContext As System.Threading.SynchronizationContext
    Private m_DestinationPath As String

    Public Sub New()

        ' This call is required by the designer.
        InitializeComponent()

        ' Add any initialization after the InitializeComponent() call.
        Me.m_SyncContext = System.Threading.SynchronizationContext.Current
    End Sub

    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        Me.m_DestinationPath = Me.TextBox2.Text
        Dim directoryPath As String = Path.GetDirectoryName(TextBox1.Text)
        Dim varFileSystemWatcher As New FileSystemWatcher()
        varFileSystemWatcher.Path = directoryPath

        varFileSystemWatcher.NotifyFilter = (NotifyFilters.LastWrite)
        varFileSystemWatcher.Filter = Path.GetFileName(TextBox1.Text)
        AddHandler varFileSystemWatcher.Changed, AddressOf OnChanged
        varFileSystemWatcher.EnableRaisingEvents = True
    End Sub

    Private Sub OnChanged(source As Object, ByVal e As FileSystemEventArgs)
        My.Computer.FileSystem.CopyFile(e.FullPath, Me.m_DestinationPath & "\" & e.Name, True)
        Me.m_SyncContext.Post(AddressOf UpdateTextBox, "[New Text]")
    End Sub

    Private Sub UpdateTextBox(param As Object)
        If (TypeOf (param) Is String) Then
            Me.TextBox3.AppendText(CStr(param))
        End If
    End Sub
End Class