I have a program that is supposed to read from a number of TCPIP connections and display the parsed data on datagridviews. I created a class DataReader that creates the connection and launches a thread to read the data. I protected the bindinglist(of Data) (data is a class that will parse a string into different properties) with a mutex which I'm not sure I'm using right since it's a new concept to me and a synclock. When I start up the DataReader classes the datagridviews populate but then I get an error saying "Cross-thread operation not valid: Control '' accessed from a thread other than the thread it was created on.". The stack trace points to the line that adds new data to the bindinglist(of Data). Also if it matters the data can't be changed from the UI thread only populated from the DataReader. Any help would be greatly appreciated!
the databinding looks like this:
myDataGridView1.datasource = myDataReader1.DataList
Here is the DataReader class(I know it's a lot of code but I didn't want to leave out something important):
Public Class DataReader
Implements INotifyPropertyChanged
#Region "Properties"
Dim dataMutex As Mutex
Dim UnparsedMutex As Mutex
Dim mIP_Address As String
Dim convertingData As Boolean = False
Public Property IP_Address() As String
Get
Return mIP_Address
End Get
Set(ByVal value As String)
mIP_Address = value
End Set
End Property
Dim mPort As Integer
Public Property Port As Integer
Get
Return mPort
End Get
Set(ByVal value As Integer)
mPort = value
End Set
End Property
Private WithEvents mDataList As BindingList(Of Data)
Public ReadOnly Property DataList As BindingList(Of Data)
Get
Return mDataList
End Get
End Property
Public Event valueChanged As Eventhandler
Private Event NewDataAvaiable(ByVal newData As BindingList(Of Data))
Private mUnparsedData As String = ""
Private Property UnParsedData As String
Get
Return mUnparsedData
End Get
Set(ByVal value As String)
mUnparsedData = value
End Set
End Property
#End Region
#Region "Private Variables"
Dim mTCPIPClient As TcpClient
Dim mConnected As Boolean
Dim mTCPIPStream As NetworkStream
Dim mLastError As String
Dim mDataThread As System.Threading.Thread
Private dataLock As New Object
Private Delegate Sub AddNewDataDelegate(ByVal DataToAdd As BindingList(Of Data))
#End Region
#Region "Constructors"
Public Sub New(ByVal IPAddress As String, ByVal Port As Integer)
dataMutex = New Mutex(False, "MUTEXDATA")
UnparsedMutex = New Mutex(False, "MUTEXUNPARSEDDATA")
mIP_Address = IPAddress
mPort = Port
mConnected = False
mDataList = New BindingList(Of Data)
DataList.RaiseListChangedEvents = True
End Sub
#End Region
#Region "Events"
Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
Protected Sub OnPropertyChanged(ByVal name As String)
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(name))
End Sub
#End Region
#Region "Methods"
Public Sub ConnectTCPIP()
Try
If Not mTCPIPClient Is Nothing AndAlso mTCPIPClient.Connected Then
mLastError = "Connection Error:Already connected"
Exit Sub
ElseIf String.IsNullOrEmpty(mIP_Address) Then
mLastError = "Connection Error:No IP Address"
Exit Sub
End If
DataList.Clear()
UnParsedData = String.Empty
mTCPIPClient = New TcpClient()
mTCPIPClient.Connect(mIP_Address, mPort)
If mTCPIPClient.Connected Then
mTCPIPClient.ReceiveTimeout = 500
mTCPIPClient.SendTimeout = 500
mTCPIPClient.LingerState = New System.Net.Sockets.LingerOption(False, 0)
mTCPIPClient.ReceiveBufferSize = 100000
mTCPIPClient.SendBufferSize = 100000
mTCPIPStream = mTCPIPClient.GetStream
LaunchDataThread()
If mDataThread.IsAlive Then mConnected = True
Else
mLastError = "Connection Error:Unknown Reason"
Exit Sub
End If
Catch ex As Exception
MessageBox.Show(ex.Message & ex.StackTrace)
End Try
End Sub
Public Sub Disconnect()
DisconnectTCPIP()
KillDataThread()
End Sub
Private Sub DisconnectTCPIP()
If Not mTCPIPClient Is Nothing AndAlso mTCPIPClient.Connected Then
mTCPIPClient.Close()
mTCPIPClient = Nothing
End If
End Sub
Public Function IsConnected() As Boolean
If mTCPIPClient Is Nothing OrElse mDataThread Is Nothing Then Return False
Return mTCPIPClient.Connected AndAlso mDataThread.IsAlive
End Function
Public Sub LaunchDataThread()
mDataThread = New System.Threading.Thread(AddressOf ReadData)
mDataThread.Start()
End Sub
Public Sub KillDataThread()
If Not mDataThread Is Nothing AndAlso mDataThread.IsAlive() Then
mDataThread.Abort()
End If
mDataThread = Nothing
End Sub
Public Sub ReadData()
Try
While True
Dim count As Short = 0
While Not mTCPIPClient.Connected AndAlso count <= 5
System.Threading.Thread.Sleep(5000)
ConnectTCPIP()
count += 1
End While
If mTCPIPClient.Connected = False Then Exit Sub
Dim dataBuffer(500) As Byte
Dim readBytes As Integer = 0
UnparsedMutex.WaitOne()
Do While mTCPIPStream.DataAvailable
readBytes = mTCPIPStream.Read(dataBuffer, 0, 500)
UnParsedData += System.Text.Encoding.ASCII.GetString(dataBuffer.Take(readBytes).ToArray())
Loop
UnparsedMutex.ReleaseMutex()
If UnParsedData.Length > 0 Then ProcessNewData()
System.Threading.Thread.Sleep(500)
End While
Catch ex As Exception
Windows.Forms.MessageBox.Show(ex.Message + Environment.NewLine + ex.StackTrace)
End Try
End Sub
Public Sub ProcessNewData()
Dim dataToProcess As String = ""
UnparsedMutex.WaitOne()
If String.IsNullOrEmpty(UnParsedData) Then
UnparsedMutex.ReleaseMutex()
Exit Sub
End If
dataToProcess = UnParsedData.Substring(0, UnParsedData.LastIndexOf(CHR(3))
UnParsedData = UnParsedData.Remove(0, dataToProcess.Length)
UnparsedMutex.ReleaseMutex()
If Not String.IsNullOrEmpty(dataToProcess) Then
Dim matches = dataToProcess.Split(";"c)
If matches.Count > 0 Then
Dim newData As New BindingList(Of Data)
Try
For i = 0 To matches.Count - 1
newData.Insert(0, New STS_Crane_Data(Name, matches(i).Value, Me.Format))
Next
Dim NewDataDel As New AddNewDataDelegate(AddressOf AddProcessedData)
NewDataDel.Invoke(newData)
Catch ex As Exception
Windows.Forms.MessageBox.Show(ex.Message + Environment.NewLine + ex.StackTrace)
End Try
End If
End If
End Sub
Private Sub AddProcessedData(ByVal newData As BindingList(Of STS_Crane_Data))
SyncLock dataLock
dataMutex.WaitOne()
For Each dataItem As Data In newData
DataList.Insert(0, dataItem)
If DataList.Count > 5000 Then DataList.RemoveAt(5000)
Next
dataMutex.ReleaseMutex()
End SyncLock
End Sub
#End Region
End Class