我的服务器应用程序是一个WPF项目,它使用异步回调来处理客户端请求并将响应发送回客户端。
服务器将根据从客户端接收到的数据更新其数据库,并根据数据的性质,通过其自己的UI推动警报以反映新的警报。
是时候更新UI时会遇到一个错误,说该通话线程必须是sta线程或某些此类内容。
如何确保试图更新UI的线程可以正确设置以执行此操作而不会引起错误?
有关我的所有代码和评论,请参见下文。
客户助手
客户端汉牌类是客户端请求的包装器。
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);
}
读取客户端数据后,服务器执行操纵数据并将警报转发到UI的功能。HandleClientData
是所有这些开始的地方。这是服务器所做的最后一个读物。
private void ReadCallback(IAsyncResult result)
{
ClientHelper Client = result.AsyncState as ClientHelper;
if (Client != null)
{
NetworkStream Stream = Client.NetworkStream;
int Read;
try
{
Read = Stream.EndRead(result);
}
catch (Exception ex)
{
OnError(Client, ex);
return;
}
if (Read == 0)
{
Client.TcpClient.Close();
ConnectedClients.Remove(Client);
return;
}
byte[] Data = new byte[Read];
Buffer.BlockCopy(Client.Buffer, 0, Data, 0, Read); // copy read data to the client's buffer
Stream.BeginRead(Client.Buffer, 0, Read, ReadCallback, Client); // read data
HandleClientData(Stream, Encoding.ASCII.GetString(Client.Buffer, 0, Data.Length));
}
}
private void HandleClientData(NetworkStream stream, string data)
{
byte[] value = null;
try
{
string[] Data = data.Split(',');
if (String.Equals(Data[0], "GetAllFoo"))
{
value = Encoding.ASCII.GetBytes(GetFoo());
}
else if (String.Equals(Data[0], "GetAFoo"))
{
int FooId;
Int32.TryParse(Data[1], out FooId);
value = Encoding.ASCII.GetBytes(GetFoo(FooId));
}
else
{
// Update the Foo in the database to reflect the latest state of every component.
UpdateFoo(Data);
// evaluate the data for a fault and raise an alert if there's something wrong.
if (!EvaluateFooData(Data[1]))
{
AddAlert();
}
value = Encoding.ASCII.GetBytes("SUCCESS,The Foo was successfully updated.|");
}
stream.Write(value, 0, value.Length);
}
catch (Exception ex)
{
string Error = String.Format("ERR,{0}", ex.Message);
byte[] ErrorBytes = Encoding.ASCII.GetBytes(Error);
stream.Write(ErrorBytes, 0, ErrorBytes.Length);
return;
}
}
}
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_RaiseAlert
通过更新UI来更新UI,通过更改窗口初始化期间创建的按钮的样式,以便这些按钮现在表明该FOO的问题。基本概念是绿色=好,红色=坏。
执行操纵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
来自何处。