我正在为一些代码编写的代码中添加一个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
时,几乎没有发生。甚至不是例外 - 但重要的是,消息Grid不会被创建。
这是怎么回事?我在另一个SO问题的建议中添加Dispatcher.BeginInvoke
以确保线程所有权不是问题,尽管在此之前发生了相同的事情。
(注意:所有MessageBox
es仅用于调试。有趣的是,MessageBox
顶部的DisplayMessage
会在收听者调用时显示线程。)
答案 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
。