简体   繁体   English

为什么不创建此Grid / TextBlock?

[英]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: 该计划如此流动:

  1. 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); } } 
  2. 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); } 
  3. 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); } } } 
  4. 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电话等ProcessMessagewindow.InvokeDisplayMessage

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM