Reputation: 4675
I have a Window with a Rich Text Box. How can I open it in a New Thread, then write to the Text Box?
I've tried many different ways with Dispatcher.BeginInvoke and using Thread and BackgroundWorker, but I'm not setting it up right. I get errors such as "The calling thread cannot access this object because a different thread owns it." when trying to open the window or write.
I start a hidden background window using Hide(), open with Show():
MainWindow mainwindow = this;
myWindow = new NewWindow(mainwindow);
myWindow.Left = this.Left + 605;
myWindow.Top = this.Top + 0;
myWindow.Hide();
myWindow.myRichTextBox.Cursor = Cursors.Arrow;
I write to it using Actions that are saved to a list, then called at a certain time:
List<Action> LogActions = new List<Action>();
Action WriteAction;
// Create Multiple Actions
WriteAction = () =>
{
myWindow.myRichTextBox.Document = new FlowDocument(paragraph);
paragraph.Inlines.Add(new LineBreak());
paragraph.Inlines.Add(new Bold(new Run("Example")) { Foreground = Brushes.White });
};
// Add Actions to List
LogActions.Add(WriteAction);
// Write All Actions in List
foreach (Action Write in LogActions)
{
Write();
}
To simplify, it is basically doing:
myWindow.myRichTextBox.AppendText("Example");
It is able to pass data without new thread right now. But the mouse freezes for a few seconds when the Rich Text Box is being written to, I thought this might free it up. Would anyone advise against opening in new thread?
Upvotes: 0
Views: 343
Reputation: 684
It's usually best practice for the user interface to stay in the main application thread, where the UI can spawn background threads to perform longer-running tasks. This way, the UI thread can still respond to user input while the background work is underway.
In the case of a Window, myWindow
with a Rich Text Box that you want to append messages to, myWindow
would use a BackgroundWorker
to gather the messages in a separate thread and then update the RTF control through a callback on thread termination (for a BackgroundWorker
this is the OnRunWorkerCompleted
method).
If the window must run as a separate thread, this helper class will wrap a WPF Window in a new thread and set it up with a new SynchronizationContext
, however, you will still need thread-safe messaging if you want to communicate between windows in different threads. The ConcurrentQueue<T>
collection provides a thread-safe collection that may be useful for this purpose.
public static class WindowThreadHelpers
{
public static void LaunchWindowInNewThread<T>() where T : Window, new()
{
Dispatcher.CurrentDispatcher.Invoke(new ThreadStart(ThreadStartingPoint<T>));
}
private static void ThreadStartingPoint<T>() where T : Window, new()
{
SynchronizationContext.SetSynchronizationContext(
new DispatcherSynchronizationContext(
Dispatcher.CurrentDispatcher));
var win = new T();
win.Closing += (sender, args) =>
{
Dispatcher.ExitAllFrames();
win.Dispatcher.InvokeShutdown();
};
win.Show();
Dispatcher.Run();
}
}
Here's an example of how you would use it from the main thread
WindowThreadHelpers.LaunchWindowInNewThread<NewWindow>();
Upvotes: 2