不久前,我正在为我为信使编写的一些代码添加一个 UI。除了我尝试显示来自不同线程的新消息之外,它似乎一切正常。
程序是这样运行的:
MainWindow
被创建和实例化。 MainWindow
的构造函数:public MainWindow()
{
InitializeComponent();
_messageContainer = (StackPanel)FindName("Messages");
_messageStatus = (StatusBarItem)FindName("MessageStatus");
_messageCountElement = (StatusBarItem)FindName("MessageCount");
_messageBox = (TextBox)FindName("MessageToSend");
_sendButton = (Button)FindName("SendMessage");
_ipAddress = GetIPAddress();
try
{
Client.Initialize(_ipAddress, this);
_sendButton.IsEnabled = true;
_messageBox.IsEnabled = true;
}
catch (Exception e)
{
DisplayMessage("Could not connect to " + _ipAddress.ToString() + ". The error given was '" + e.Message + "'.", "Server", Colors.Red);
}
}
Client
类)使用 Client.Initialize
构造函数中的 MainWindow
进行初始化。 Client.Initialize
:public static void Initialize(IPEndPoint endPoint, MainWindow window)
{
windowInstance = window;
client = new TcpClient();
client.ReceiveTimeout = 500;
listenerThread = new Thread(new ParameterizedThreadStart(Receiver.Start));
listenerThread.Start((object)new StartupData(endPoint, windowInstance));
client.Connect(endPoint);
}
Client.Initialize
开始。监听器线程的 Start
方法又长又复杂,但工作正常。它归结为调用另一个方法 ProcessMessage
来处理它接收到的内容。 ProcessMessage
:public static void ProcessMessage(string response)
{
response = response.Trim();
MessageBox.Show(response);
if (response.StartsWith("[Message]"))
{
MessageBox.Show("Message");
response = response.Substring(9);
int openIndex = response.IndexOf("<");
int closeIndex = response.IndexOf(">");
if (openIndex < 0 || closeIndex < 0 || closeIndex < openIndex)
{
throw new FormatException("Could not find ID tag in message");
}
int diff = closeIndex - openIndex;
int id = Int32.Parse(response.Substring(openIndex + 1, diff - 1));
if (id != Client.GetClientId())
{
MessageBox.Show("Them");
string message = response.Substring(closeIndex + 1);
window.DisplayMessage(message, "Them", Colors.Yellow);
}
else
{
MessageBox.Show("You");
string message = response.Substring(closeIndex + 1);
window.DisplayMessage(message, "You", Colors.Blue);
}
}
else if (response.Length == 5 && response.StartsWith("[") && response.EndsWith("]"))
{
MessageBox.Show("Response code");
Client.HandleResponse(ResponseCodes.GetResponse(response));
}
else
{
try
{
Int32.Parse(response);
MessageBox.Show("ID");
Client.SetClientId(Int32.Parse(response));
return;
}
catch (Exception)
{
window.DisplayMessage(response, "Server", Colors.Red);
}
}
}
DisplayMessage
方法。 DisplayMessage
:public void DisplayMessage(string message, string name, Color nameColor)
{
MessageBox.Show("Called");
UpdateMessageStatus(ProcessingStatus.Processing);
Grid fullMessage = new Grid();
fullMessage.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(50.00) });
fullMessage.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(600.00) });
Label nameLabel = new Label
{
Content = string.Format("[{0}]", name),
Foreground = new SolidColorBrush(nameColor)
};
Grid.SetColumn(nameLabel, 0);
TextBlock textLabel = new TextBlock
{
Text = message,
TextWrapping = TextWrapping.Wrap,
Margin = new Thickness(0, 5, 0, 5)
};
Grid.SetColumn(textLabel, 1);
fullMessage.Children.Add(nameLabel);
fullMessage.Children.Add(textLabel);
UpdateMessageStatus(ProcessingStatus.Displaying);
Dispatcher.BeginInvoke(new Action(delegate()
{
_messageContainer.Children.Add(fullMessage);
}));
_messageCount += 1;
UpdateMessageCount();
UpdateMessageStatus(ProcessingStatus.Ready);
}
这是问题所在。当我从监听器线程调用
window.DisplayMessage
时,实际上什么也没发生。甚至不是一个异常(exception) - 但重要的是,消息 Grid 没有被创建。这是怎么回事?我根据另一个 SO 问题的建议添加了
Dispatcher.BeginInvoke
以确保线程所有权不是问题,尽管在此之前发生了同样的事情。(注意:所有
MessageBox
es 仅用于调试。有趣的是,当从监听器线程调用时,MessageBox
顶部的 DisplayMessage
确实显示。) 最佳答案
我确实怀疑这是跨线程 UI 元素调用的问题。由于您是在不同的线程上创建新的 UI 元素( fullMessage
及其子元素)。
如果您的 window
实例是 WPF UserControl
或 WPF Window
,您可以使用它的同步上下文来执行跨线程编码(marshal)处理。
MainWindow
构造函数中捕获其同步上下文_syncContext = SynchronizationContext.Current;
public void InvokeDisplayMessage(string message, string name, Color nameColor)
{
//本着已经开始的消息框调试精神;-)
MessageBox.Show("调用显示消息");
this._syncContext.Post(
new SendOrPostCallback(x => DisplayMessage(message, name, nameColor)),
空值);
}
window.DisplayMessage
中的所有 ProcessMessage
调用更改为window.InvokeDisplayMessage
。 关于c# - 为什么不创建此 Grid/TextBlock?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/30017352/