C# 从异步操作调用时,如何从STA线程更新窗口控件?
我的服务器应用程序是一个WPF项目,它使用异步回调来处理客户端请求并将响应发送回客户端 服务器将根据从客户端接收的数据更新其数据库,并根据数据的性质,通过其自己的UI推送警报以反映新警报 当需要更新UI时,我得到一个错误,即调用线程必须是STA线程或类似的东西 如何确保尝试更新UI的线程已正确设置为在不导致错误的情况下进行更新 下面是我所有的代码和关于代码的注释 客户助手 ClientHelper类是客户端请求的包装器C# 从异步操作调用时,如何从STA线程更新窗口控件?,c#,wpf,multithreading,C#,Wpf,Multithreading,我的服务器应用程序是一个WPF项目,它使用异步回调来处理客户端请求并将响应发送回客户端 服务器将根据从客户端接收的数据更新其数据库,并根据数据的性质,通过其自己的UI推送警报以反映新警报 当需要更新UI时,我得到一个错误,即调用线程必须是STA线程或类似的东西 如何确保尝试更新UI的线程已正确设置为在不导致错误的情况下进行更新 下面是我所有的代码和关于代码的注释 客户助手 ClientHelper类是客户端请求的包装器 public class ClientHelper { privat
public class ClientHelper
{
private readonly TcpClient _Client;
private readonly byte[] _Buffer;
public Client(TcpClient client)
{
_Client = client;
int BufferSize = _Client.ReceiveBufferSize;
_Buffer = new byte[BufferSize];
}
public TcpClient TcpClient
{
get { return _Client; }
}
public byte[] Buffer
{
get { return _Buffer; }
}
public NetworkStream NetworkStream
{
get { return TcpClient.GetStream(); }
}
}
FooServer
服务器使用在其自身线程上运行的TcpListener,以避免锁定UI
public class FooServer
{
private TcpListener Svr;
public void StartServer()
{
Thread ListenerThread = new Thread(new ThreadStart(() =>
{
Svr = new TcpListener(IPAddress.Parse("127.0.0.1"), 13000);
Svr.Start();
Svr.BeginAcceptTcpClient(AcceptClientCallback, null);
}));
ListenerThread.SetApartmentState(ApartmentState.STA);
ListenerThread.IsBackground = true;
ListenerThread.Start();
}
服务器通过维护其连接的客户端列表来跟踪这些客户端
private List<Client> ConnectedClients = new List<Client>();
private void AcceptClientCallback(IAsyncResult result)
{
TcpClient Client;
try
{
Client = Svr.EndAcceptTcpClient(result);
}
catch (Exception ex)
{
OnError(Svr, ex);
//Svr.Stop();
return;
}
Svr.BeginAcceptTcpClient(AcceptClientCallback, null);
ClientHelper _Client = new ClientHelper(Client);
ConnectedClients.Add(_Client);
NetworkStream Stream = _Client.NetworkStream;
Stream.BeginRead(_Client.Buffer, 0, _Client.Buffer.Length, ReadCallback, _Client);
}
EvaluateFooData
根据可接受的标准检查客户数据,并将任何偏差添加到列表中,该列表由下面的AddAlert
读取,该列表将警报添加到数据库中
public void AddAlert()
{
ApplicationDbContext Context = new ApplicationDbContext();
foreach (Alert Alert in Alerts)
{
Context.Alerts.Add(Alert);
}
Context.SaveChanges();
OnRaiseAlert();
}
public event EventHandler RaiseAlert;
protected virtual void OnRaiseAlert()
{
RaiseAlert?.Invoke(this, null);
}
使用在UI上注册的EventHandler,服务器将警报推送到UI:
public MainWindow()
{
InitializeComponent();
Server.RaiseAlert += Server_RaiseAlert;
}
private void Server_RaiseAlert(object sender, EventArgs e)
{
ApplicationDbContext Context = new ApplicationDbContext();
var Alerts = Context.Alerts.Where(x => x.IsResolved == false).ToList();
StackPanel FooStackPanel = new StackPanel();
spFoo.Dispatcher.Invoke(DispatcherPriority.Normal, new Action(() => { FooStackPanel = spFoo; }));
if (Alerts != null && Alerts.Count >= 1)
{
foreach (Alert Alert in Alerts)
{
Button Btn = (Button)FooStackPanel.Children[FooId];
Btn.Style = FindResource("FooAlertIcon") as Style;
}
}
}
Server\u RaiseAlert
通过更改窗口初始化期间创建的按钮样式来更新UI,以便这些按钮现在指示该Foo存在问题。基本概念是绿色=好,红色=坏。在Dispatcher操作中处理UI元素的所有操作:
private void Server_RaiseAlert(object sender, EventArgs e)
{
var context = new ApplicationDbContext();
var alerts = context.Alerts.Where(x => x.IsResolved == false).ToList();
if (alerts.Count > 0)
{
spFoo.Dispatcher.Invoke(new Action(() =>
{
foreach (var alert in alerts)
{
var button = (Button)spFoo.Children[FooId];
button.Style = FindResource("FooAlertIcon") as Style;
}
}));
}
}
但是请注意,从您的问题中不清楚
FooId
来自何处。您必须更具体地说明错误。哪一个错误,在哪一行?STA线程必须有一个消息泵,我在您创建的线程中看不到消息泵。请注意,从您的问题中不清楚FooId来自何处。
这不是问题。只是我的粘贴不完整,先生,你应该得到更多,而不仅仅是对这个答案投一张有用的票。我不能告诉你我有多激动,因为它现在可以工作了!非常感谢你。
private void Server_RaiseAlert(object sender, EventArgs e)
{
var context = new ApplicationDbContext();
var alerts = context.Alerts.Where(x => x.IsResolved == false).ToList();
if (alerts.Count > 0)
{
spFoo.Dispatcher.Invoke(new Action(() =>
{
foreach (var alert in alerts)
{
var button = (Button)spFoo.Children[FooId];
button.Style = FindResource("FooAlertIcon") as Style;
}
}));
}
}