[英]Why does this Grid/TextBlock not get created?
I'm adding a UI to some code I wrote for a messenger, some while ago. 我正在为一些信使添加一个用户界面,不久之前。 It seems to be going OK, apart from when I try to display new messages from a different thread.
除了当我尝试从不同的线程显示新消息时,它似乎还可以。
The program flows thusly: 该计划如此流动:
The MainWindow
gets created and instantiated. MainWindow
被创建并实例化。 The constructor for 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); } }
The sending client ( Client
class) is initialized using Client.Initialize
from the MainWindow
constructor. 使用
MainWindow
构造函数中的Client.Initialize
初始化发送客户端( Client
类)。 Client.Initialize
: 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); }
The listener thread is started from Client.Initialize
. 侦听器线程从
Client.Initialize
启动。 The listener thread's Start
method is long and complicated, but works fine. 侦听器线程的
Start
方法很长且很复杂,但工作正常。 It boils down to calling another method, ProcessMessage
to process what it receives. 归结为调用另一个方法
ProcessMessage
来处理它收到的内容。 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); } } }
The DisplayMessage
method is called. 调用
DisplayMessage
方法。 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); }
Here is the problem. 这是问题所在。 When I call
window.DisplayMessage
from the listener thread, literally nothing happens. 当我从侦听器线程调用
window.DisplayMessage
, 几乎没有任何反应 。 Not even an exception - but importantly, the message Grid doesn't get created. 甚至不是例外 - 但重要的是,消息Grid不会被创建。
What's up with this? 怎么了? I added
Dispatcher.BeginInvoke
on the advice of another SO question to make sure the thread ownership wasn't a problem, though the same thing happened before this. 我在另一个SO问题的建议上添加了
Dispatcher.BeginInvoke
以确保线程所有权不是问题,尽管在此之前发生了同样的事情。
(note: all the MessageBox
es are just for debugging. Interestingly, the MessageBox
at the top of DisplayMessage
does show when called from the listener thread.) (注意:所有
MessageBox
都只是用于调试。有趣的是, DisplayMessage
顶部的MessageBox
确实显示何时从侦听器线程调用。)
I do suspect this is an issue of cross-thread UI element invokation. 我怀疑这是跨线程UI元素调用的问题。 Since you are creating your new UI element (
fullMessage
and its children) on a different thread. 因为您在不同的线程上创建新的UI元素(
fullMessage
及其子元素)。
If your window
instance is a WPF UserControl
or a WPF Window
you can use it's synchronization context to perform the cross-thread marshaling. 如果您的
window
实例是WPF UserControl
或WPF Window
您可以使用它的同步上下文来执行跨线程编组。
Capture its synchronization context in your MainWindow
constructor 在
MainWindow
构造函数中捕获其同步上下文
_syncContext = SynchronizationContext.Current;_syncContext = SynchronizationContext.Current;
Add a method that uses the captured context, to marshal to the correct dispatcher. 添加使用捕获的上下文的方法,以编组到正确的调度程序。
\npublic void InvokeDisplayMessage(string message, string name, Color nameColor)public void InvokeDisplayMessage(字符串消息,字符串名称,颜色nameColor)\n{
{\n // In the already started spirit of message box debugging ;-)
//在已启动的消息框调试精神中;-)\n MessageBox.Show("InvokeDisplayMessage Called");
MessageBox.Show(“InvokeDisplayMessage Called”);\n this._syncContext.Post(
this._syncContext.Post(\n new SendOrPostCallback(x => DisplayMessage(message, name, nameColor)),
新的SendOrPostCallback(x => DisplayMessage(message,name,nameColor)), \n null);
空值);\n}
}\n
And finally change all of the window.DisplayMessage
calls in your ProcessMessage
to window.InvokeDisplayMessage
. 最后更改所有的
window.DisplayMessage
电话等ProcessMessage
到window.InvokeDisplayMessage
。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.