【问题标题】:Update TextBox in Runtime. Cross-thread operation not valid在运行时更新文本框。跨线程操作无效
【发布时间】:2018-03-21 17:02:06
【问题描述】:

我正在做一个 TCP 连接客户端/服务器,我真的想在运行时更新标签或文本框,因为我正在使用一个工作正常的委托,但是当我尝试更新 Form1 时,它根本不像我一样工作喜欢。

在下面的代码中,我解释了我在做什么。

我有一个名为 Form1 的类:

Public Class Form1
Public server As tcp_server
Private Shared _instance As Form1 = Nothing

Public Shared ReadOnly Property MainInstance As Form1
    Get
        Return _instance
    End Get
End Property

Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load

    If Form1._instance Is Nothing Then
        Form1._instance = Me 'Setting the main instance if none exists.
    End If

    server = New tcp_server("192.168.30.31", "5000")
    server.connect()

End Sub

Delegate Sub SetTextCallback(ByVal textBox As TextBox, ByVal text As String)

Public Sub SetText(ByVal textBox As TextBox, ByVal text As String)

    Try
        If textBox.InvokeRequired Then
            Dim d As SetTextCallback = New SetTextCallback(AddressOf SetText)
            Me.Invoke(d, New Object() {textBox, text})
        Else
            textBox.AppendText(text)


            'Me.ShowDialog()

            Form1.MainInstance.Visible = False
            Form1.MainInstance.ShowDialog()
        End If

    Catch ex As Exception

    End Try

End Sub

End Class

错误发生在这里Form1.MainInstance.ShowDialog()

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

还有其他类名为tcp_server 的方法:

 Public Sub connect()
    Try
        Dim server_ipendpoint As New IPEndPoint(IPAddress.Parse(m_ip_host), m_port_host)
        tcpLsn = New TcpListener(server_ipendpoint)
        tcpLsn.Start()

        tcpThd = New Thread(AddressOf waiting_client)
        tcpThd.Start()
        'Form1.Label2.Text = "Listening"
        Form1.TextBox1.Text = "Listening" & vbCrLf

    Catch ex As Exception
        is_exception = True
    End Try

End Sub

如您所见,在这种方法中,我开始监听传入的连接。

新线程被称为waiting_client

Private Sub waiting_client()
    Try
        Dim change_name_label As New Form1.SetTextCallback(AddressOf Form1.SetText)
        change_name_label.Invoke(Form1.TextBox1, "helllllooooooooo")

    Catch ex As Exception
    End Try
End Sub

我无法在运行时更新TextBox1。该应用程序不会刷新 Windows 窗体。

我唯一拥有的就是这个,它正在做的事情:

Public Sub SetText(ByVal textBox As TextBox, ByVal text As String)

    Try
        If textBox.InvokeRequired Then
            Dim d As SetTextCallback = New SetTextCallback(AddressOf SetText)
            Me.Invoke(d, New Object() {textBox, text})
        Else
            textBox.AppendText(text)
            Me.ShowDialog()
        End If

    Catch ex As Exception
    End Try
End Sub

但这并不是真正想要的结果。

我要更新TextBox1,我的意思是改变Listening --> helllloooooo

我看了很多帖子都找不到解决办法。

所有 cmets 或想法将不胜感激。

谢谢。

【问题讨论】:

  • 您无法通过其默认实例从后台线程访问表单。 Find out reason for form instancing 的可能重复项
  • 我已经阅读了这篇文章,但它根本没有帮助我,我尝试做同样的事情但没有工作。感谢您的来信。
  • 它绝对可以帮助你。在我的回答中,我描述了导致您的代码以当前方式运行的确切现象,即您无法通过后台线程中的默认实例访问Form1(或任何其他形式)。你的问题真的没有其他答案。 -- 还请定义“不工作”。您在哪个部分遇到了问题?
  • “默认实例”是指直接通过表单名称访问表单,而不必创建它的实例,例如Dim newForm As New Form1。 VB.NET 是唯一一种允许您通过Form1 直接访问它的语言(即您可以调用Form1.Show() 而不是myForm.Show()),如果您不知道它们的具体行为方式,这实际上是一个相当大的问题.这里有很多关于 SO 的问题,根本原因只是缺乏关于默认实例如何工作的知识(因为很难知道,特别是因为它没有很好的文档记录)。
  • 对不起,我在度假。我已经尝试了您在那篇文章中所说的所有内容,但是仍然出现相同的错误跨线程操作无效...。我无法在运行时更新 TextBox,我发现的唯一方法是我在帖子中确实提到过。我是这类问题的初学者,我没有找到其他解决方案。我真的很感激任何帮助或解决这个问题的方法。谢谢视觉文森特。

标签: vb.net tcp delegates thread-safety


【解决方案1】:

我不知道你为什么到处都调用ShowDialog(),但MainInstance 属性的目的是用Form1.MainInstance 替换每个 Form1 调用。

具体是这一行导致错误:

change_name_label.Invoke(Form1.TextBox1, "helllllooooooooo")

因为也需要改成:

change_name_label.Invoke(Form1.MainInstance.TextBox1, "helllllooooooooo")

此外,您实际上不必创建上面使用的change_name_label 委托实例,您可以简单地调用:

Form1.MainInstance.SetText(Form1.MainInstance.TextBox1, "helllllooooooooo")

...这就够了。 SetText() 方法已经为您完成了剩下的工作。

现在,要使您的代码正常工作,您需要做的最后一件事是摆脱 ShowDialog() 调用。您还应该删除 SetText()waiting_client() 方法中的 Try/Catch,因为在任何地方添加空的都是不好的做法。如果您要在可能发生错误的地方添加Try/Catch,您应该通过将其记录或向用户显示(或两者兼而有之)来正确处理它。

Form1:

Private Delegate Sub SetTextCallback(ByVal textBox As TextBox, ByVal text As String)

Public Sub SetText(ByVal textBox As TextBox, ByVal text As String)
    If textBox.InvokeRequired Then
        Dim d As New SetTextCallback(AddressOf SetText)
        textBox.Invoke(d, New Object() {textBox, text})
    Else
        textBox.AppendText(text)
    End If
End Sub

(注意Dim d As SetTextCallback = New SetTextCallback可以简写为Dim d As New SetTextCallback

tcp_server:

Public Sub connect()
    Try
        Dim server_ipendpoint As New IPEndPoint(IPAddress.Parse(m_ip_host), m_port_host)
        tcpLsn = New TcpListener(server_ipendpoint)
        tcpLsn.Start()

        tcpThd = New Thread(AddressOf waiting_client)
        tcpThd.Start()

        Form1.MainInstance.TextBox1.Text = "Listening" & vbCrLf
    Catch ex As Exception
        is_exception = True
    End Try
End Sub

Private Sub waiting_client()
    Form1.MainInstance.SetText(Form1.MainInstance.TextBox1, "helllllooooooooo")
End Sub

现在您的代码应该可以工作了!

【讨论】:

  • 我做了这个更改,但我忘记了Dim change_name_label As New Form1.SetTextCallback(AddressOf Form1.MainInstance.SetText)。无论如何,您是对的,处理一个简单的呼叫要容易得多。最后一件事,我什么时候应该使用委托,因为我了解如何使用它,但不知道什么时候是使用它的最佳时机?谢谢,非常感谢您的帮助 Visual Vincent
  • @CarlosZuazu :在这种情况下,您只需在调用Control.Invoke() 时使用委托。委托用于能够将方法视为常规对象(您可以在其中调用东西,将它们放入变量中,将它们作为参数传递给其他方法等)。现在,您将要在 UI 线程上运行的方法作为参数传递给 Control.Invoke()
猜你喜欢
  • 1970-01-01
  • 2011-07-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多