我有一个简单的WPF应用程序窗口试图读取与System.IO.Ports.SerialPort一个串行端口。

当我尝试在DataReceived事件读取传入的数据,我得到一个异常说我没有访问该线程。如何解决这个问题呢?

我有这在WPF窗口类:

Public WithEvents mSerialPort As New SerialPort()
Private Sub btnConnect_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles btnConnect.Click
    With mSerialPort
        If .IsOpen Then
            .Close()
        End If
        .BaudRate = 4800
        .PortName = SerialPort.GetPortNames()(0)
        .Parity = Parity.None
        .DataBits = 8
        .StopBits = StopBits.One
        .NewLine = vbCrLf

        .Open()
    End With
End Sub

Private Sub mSerialPort_DataReceived(ByVal sender As Object, ByVal e As System.IO.Ports.SerialDataReceivedEventArgs) Handles mSerialPort.DataReceived
    If e.EventType = SerialData.Chars Then
        txtSerialOutput.Text += mSerialPort.ReadExisting()
    End If
End Sub

Protected Overrides Sub Finalize()
    If mSerialPort.IsOpen Then
        mSerialPort.Close()
    End If
    mSerialPort.Dispose()
    mSerialPort = Nothing
    MyBase.Finalize()
End Sub

当的DataReceived事件触发,我得到mSerialPort.ReadExisting()以下异常:

System.InvalidOperationException was unhandled
  Message="The calling thread cannot access this object because a different thread owns it."
  Source="WindowsBase"
  StackTrace:
       at System.Windows.Threading.Dispatcher.VerifyAccess()    at System.Windows.Threading.DispatcherObject.VerifyAccess()    at System.Windows.DependencyObject.GetValue(DependencyProperty dp)    at System.Windows.Controls.TextBox.get_Text()    at Serial.Serial.mSerialPort_DataReceived(Object sender, SerialDataReceivedEventArgs e) in D:\SubVersion\VisionLite\Serial\Serial.xaml.vb:line 24    at System.IO.Ports.SerialPort.CatchReceivedEvents(Object src, SerialDataReceivedEventArgs e)    at System.IO.Ports.SerialStream.EventLoopRunner.CallReceiveEvents(Object state)    at System.Threading._ThreadPoolWaitCallback.WaitCallback_Context(Object state)    at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)    at System.Threading._ThreadPoolWaitCallback.PerformWaitCallbackInternal(_ThreadPoolWaitCallback tpWaitCallBack)    at System.Threading._ThreadPoolWaitCallback.PerformWaitCallback(Object state)
有帮助吗?

解决方案

<强>欢迎使用多线程的神奇世界!!!

这是怎么回事的是,所有的UI元素(类实例)的,才可以访问/通过UI线程更新。我不会进入这个线程关联的细节,但它的一个重要的课题,你应该检查一下。

,其中数据即将在串行端口上的事件正在发生在不同的线程比UI线程。 UI线程有处理窗口消息(例如鼠标点击等)的消息泵。您的串口不欢送窗口消息。当数据进入的串行端口上从UI线程一个完全不同的线程被用于处理该消息。

因此,在应用程序中,该mSerialPort_DataReceived方法是在不同的线程比你的UI线程上执行。您可以使用线程调试窗口,以验证这一点。

当您尝试更新UI,您要修改与从不同的线程,它抛出你见过异常的UI线程的线程亲和力的控制。

TL; DR:您正在试图修改UI线程之外的UI元素。使用

txtSerialOutput.Dispatcher.Invoke

要运行在UI线程上的更新。 这里有一个关于如何做到thisin这个社区内容的例子页。

在调度程序将调用你在UI线程上的方法(它发送一个窗口消息到UI说:“海guize,运行此方法kthx”),然后你的方法可以安全地更新从UI线程的UI。

其他提示

UI可以仅由主应用程序线程进行更新。串行端口事件的异步回调在后台处理上一个单独的线程。威尔提到的,可以使用Dispatcher.Invoke排队UI线程上的UI组件属性的变化。

不过,由于你使用WPF,有使用绑定一个更优雅和地道的解决方案。假设你收到串行端口上的数据有一些显著的价值,您的业务对象,你可以有DataReceived事件检索对象中的更新属性,然后将UI绑定到该属性。

在粗糙代码:

Public Class MySerialData
  Implements System.ComponentModel.INotifyPropertyChanged
  Public Event PropertyChanged(sender as Object, e as System.ComponentModel.PropertyChangedEventArgs) Implements System.ComponentModel.INotfifyPropertyChanged.PropertyChanged

  private _serialData as String
  Public Property SerialData() As String
    Get
      Return _serialData
    End Get
    Set(value as String)
      If value <> _serialData Then
        _serialData = value
        RaiseEvent PropertyChanged(Me, New ComponentModel.PropertyChangedEventArgs("SerialData"))
      End If
  End Property

然后在你的XAML文件,您可以将文本框绑定到该对象的属性:

<TextBox Text="{Binding Path=SerialData}"/>

此假定DataContext设置到您MySerialData类的一个实例。这样做水暖的这种额外位伟大的事情是,WPF,现在将处理所有的跨线程封送处理为您自动的,所以你不必担心哪个线程调用UI的变化,在WPF绑定引擎只是使它工作。显然,如果这仅仅是一个扔掉的项目可能不值得的前期码额外位。但是,如果你正在做大量的异步通信的和更新的UI WPF的这个功能是一个真正的生命的救星,并消除了一大类常见的多线程应用程序的错误。我们在一个高度线程化的应用做了很多TCP通信的使用WPF和WPF的绑定是伟大尤其是当翻过了传输的数据是指以更新UI几个地方,因为你不必有线程检查整个洒您的代码。

scroll top