I am writing a wpf application to control an embedded device over TCP. Writing to the device is easy, but I am having trouble receiving packets.
I have created a tcp NetworkStream and am using NetworkStream.BeginRead() to listen for TCP data. However, once I have composed a full packet, I would like to update my GUI to reflect the new information. It seems that I am not allowed to do this from the asynchronous callback thread.
It appears that there is a way to do this through the dispatcher one request at a time, but I need to update practically every control on my GUI. I am not writing a dispatcher function for every packet case and every WPF control. How do I get complete access to the WPF controls from my asynchronous thread?
EDIT:
Here is a code sample:
NetworkUnion union = (NetworkUnion)ar.AsyncState;
union.BytesRead += tcpStream.EndRead(ar);
if (union.BytesRead < union.TargetSize)
tcpStream.BeginRead(union.ByteArray, union.BytesRead, union.TargetSize - union.BytesRead, new AsyncCallback(ReadCommandCallback), union);
else
{
NetworkUnion payload = new NetworkUnion();
NetworkPacket pkt = (NetworkPacket)union.getArrayAsStruct();
// Respond to the packet
// Read the payload
payload.ByteArray = new byte[pkt.payloadSize];
tcpStream.Read(payload.ByteArray, 0, pkt.payloadSize);
// Determine what the payload is!
switch (pkt.code)
{
case (int)PacketCode.STATE:
payload.ObjectType = typeof(NetworkState);
NetworkState state = (NetworkState)payload.getArrayAsStruct();
Handle.fuelGauge.Value = Convert.ToDouble(state.mainFuel);
break;
When I try to update the fuel gauge, I get an InvalidOperationException, calling thread cannot access because another thread owns the object
The ‘Handle’ variable is used because this is from within a static utility class
The main question is, do I really have to replace every line of
Handle.fuelGauge.Value = Convert.ToDouble(state.mainFuel);
with
Handle.fuelGauge.Dispatcher.Invoke(
System.Windows.Threading.DispatcherPriority.Normal,
new Action(
delegate()
{
Handle.fuelGauge.Value = Convert.ToDouble(state.mainFuel);
}
));
? It seems excessively repetitive. It would be easier to do something like:
Handle.mainWindow.Dispather.Lock();
Handle.fuelGauge.Value = Convert.ToDouble(state.mainFuel);
change everything else...
Handle.mainWindow.Dispatcher.Unlock();
Generally, in .NET, you need to marshal UI requests back to the main UI thread. This is a common scenario when trying to update UI after a network read, because when using Sockets, you are usually always receiving data on some other (threadpool) thread.
So, in your code which receives the data, you should wrap up the bytes into some useful object, and marshal that back to an entry point method on the main UI thread which can then take responsibility for farming out UI updates, on the now correct thread.
There are many ways to do this, but perhaps a more flexible approach than some, would be for your callers of the socket code to pass in a SynchronizationContext object, which can then be used to marshal the received data to.
See here for more info on SynchronizationContext
You won’t need to post back every socket call, just move your swithc statement, that decides how to handle a packet, into a class on the UI thread, and invoke to that method. Then, that method can do UI updates as normal, as all calls from it are already marshalled correctly.