使用 C# 和 WPF 的异步网络
本文关键字:异步 网络 WPF 使用 | 更新日期: 2023-09-27 18:36:07
我正在编写一个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;
当我尝试更新电量计时,我收到一个无效操作异常,调用线程无法访问,因为另一个线程拥有该对象
使用"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();
通常,在 .NET 中,需要将 UI 请求封送回主 UI 线程。这是在网络读取后尝试更新 UI 时的常见方案,因为在使用 Sockets 时,通常总是在某个其他(线程池)线程上接收数据。
因此,在接收数据的代码中,您应该将字节包装到一些有用的对象中,并将其封送回主 UI 线程上的入口点方法,然后该方法可以负责在现在正确的线程上分配 UI 更新。
有很多方法可以做到这一点,但也许比某些方法更灵活的方法是让套接字代码的调用方传入 SynchronizationContext 对象,然后可以使用该对象将接收到的数据封送到。
有关同步上下文的详细信息,请参阅此处
您无需回发每个套接字调用,只需将决定如何处理数据包的 swithc 语句移动到 UI 线程上的类中,并调用该方法即可。然后,该方法可以正常执行 UI 更新,因为来自它的所有调用都已正确封送。
不,您不需要像您所说的那样替换每一行。 像这样的事情怎么样:
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;
}
));