0

我正在编写一个 wpf 应用程序来通过 TCP 控制嵌入式设备。写入设备很容易,但我无法接收数据包。

我创建了一个 tcp NetworkStream 并使用 NetworkStream.BeginRead() 来监听 TCP 数据。但是,一旦我编写了一个完整的数据包,我想更新我的 GUI 以反映新信息。似乎不允许我从异步回调线程中执行此操作。

似乎有一种方法可以通过调度程序一次一个请求来执行此操作,但我几乎需要更新我的 GUI 上的每个控件。我不是为每个数据包案例和每个 WPF 控件编写调度程序函数。如何从我的异步线程完全访问 WPF 控件?

编辑: 这是一个代码示例:

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;

当我尝试更新电量计时,我得到一个 InvalidOperationException,调用线程无法访问,因为另一个线程拥有该对象

使用“Handle”变量是因为它来自静态实用程序类

主要问题是,我真的必须更换每一行

Handle.fuelGauge.Value = Convert.ToDouble(state.mainFuel);

Handle.fuelGauge.Dispatcher.Invoke(
                                System.Windows.Threading.DispatcherPriority.Normal,
                                new Action(
                                    delegate()
                                    {
                                        Handle.fuelGauge.Value = Convert.ToDouble(state.mainFuel);
                                    }
                            ));

? 似乎过于重复了。这样做会更容易:

Handle.mainWindow.Dispather.Lock();
Handle.fuelGauge.Value = Convert.ToDouble(state.mainFuel);
change everything else...
Handle.mainWindow.Dispatcher.Unlock();
4

2 回答 2

1

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.

于 2012-06-01T21:15:55.997 回答
0

不,你不需要像你说的那样替换每一行。像这样的东西怎么样:

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!
                double yourvalue = new double(); 
                switch (pkt.code)
                {
                    case (int)PacketCode.STATE:
                        payload.ObjectType = typeof(NetworkState);
                        NetworkState state = (NetworkState)payload.getArrayAsStruct();
                        yourvalue = Convert.ToDouble(state.mainFuel);
                        break;
            /**
                Other Cases....

            */
         // After the switch 

                    Handle.fuelGauge.Dispatcher.Invoke(
                            System.Windows.Threading.DispatcherPriority.Normal,
                            new Action(
                                delegate()
                                {
                                    Handle.fuelGauge.Value = yourvalue;
                                }
                        )); 
于 2012-06-01T21:27:01.730 回答