2015-05-03 2 views
4

Я добавляю пользовательский интерфейс к некоторому коду, который я написал для мессенджера, некоторое время назад. Кажется, все идет хорошо, кроме того, когда я пытаюсь отображать новые сообщения из другого потока.Почему этот Grid/TextBlock не создается?

Программа потоков таким образом:

  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); 
    } 
    

Вот проблема. Когда я звоню window.DisplayMessage из прослушивающей нитки, буквально ничего бывает. Даже не исключение, но главное, сообщение Grid не создается.

Что случилось с этим? Я добавил Dispatcher.BeginInvoke по совету другого вопроса SO, чтобы убедиться, что ответственность за нить не является проблемой, хотя то же самое произошло до этого.

(примечание: все MessageBox эс только для отладки Интересно, что MessageBox в верхней части DisplayMessageделает шоу, когда вызывается из слушающего потока.).

+1

Вы уверены, что находитесь в потоке пользовательского интерфейса с этим вызовом BeginInvoke? Попробуйте 'Application.Current.Dispatcher.BeginInvoke' и посмотрите, дает ли это разные результаты. – Chris

ответ

2

Я подозреваю, что это проблема креста -thread UI invocation. Поскольку вы создаете новый элемент пользовательского интерфейса (fullMessage и его дочерние элементы) в другой теме.

Если window экземпляр является WPF UserControl или WPF Window вы можете использовать его контекст синхронизации для выполнения кросс-нить маршалинга.

  • Захват его контекст синхронизации в вашем MainWindow конструктору

    _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); 
    } 
    
  • И, наконец, изменить все window.DisplayMessage вызовов в ProcessMessage к window.InvokeDisplayMessage.

+0

Работает с удовольствием :) – ArtOfCode

+0

@ArtOfCode, лучший способ возможен, я обновлю свой ответ, чтобы отразить это. – Alex

Смежные вопросы