为什么不创建此Grid / TextBlock?

时间:2015-05-03 17:55:42

标签: c# wpf multithreading

我正在为一些代码编写的代码中添加一个UI,不久之前。除了当我尝试从不同的线程显示新消息时,它似乎还行。

程序如此流动:

  1. 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. 使用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);
    }
    
  3. 侦听器线程从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);
            }
        }
    }
    
  4. 调用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);
    }
    
  5. 这是问题所在。当我从侦听器线程调用window.DisplayMessage时,几乎没有发生。甚至不是例外 - 但重要的是,消息Grid不会被创建。

    这是怎么回事?我在另一个SO问题的建议中添加Dispatcher.BeginInvoke以确保线程所有权不是问题,尽管在此之前发生了相同的事情。

    (注意:所有MessageBox es仅用于调试。有趣的是,MessageBox 顶部的DisplayMessage会在收听者调用时显示线程。)

1 个答案:

答案 0 :(得分:2)

我怀疑这是跨线程UI元素调用的问题。由于您是在另一个线程上创建新的UI元素(def dictionary_insert(w): acronyms[w] = acronyms.get(w) if w in acronyms else input("\nWhat's the definition?: ") 及其子元素)。

如果您的fullMessage实例是WPF window或WPF Window,您可以使用它的同步上下文来执行跨线程封送。

  • UserControl构造函数

    中捕获其同步上下文
    _syncContext = SynchronizationContext.Current;
  • 添加一个使用捕获的上下文的方法,以编组到正确的调度程序。

    public void InvokeDisplayMessage(string message, string name, Color nameColor)
    {
        // In the already started spirit of message box debugging ;-)
        MessageBox.Show("InvokeDisplayMessage Called");
        this._syncContext.Post(
            new SendOrPostCallback(x => DisplayMessage(message, name, nameColor)), 
            null);
    }
    
  • 最后将MainWindow中的所有window.DisplayMessage来电更改为 ProcessMessage