问题
I'm writing a plug-in class for a third-party GUI application. The application calls the Run
method of my class, passing me a vendor-defined Context
object. Any methods or properties in this Context
object must be called from the current thread (that is, the application's UI thread).
My plug-in code creates a WPF Window
and shows it to the user. Some interactions need to call the Context
object's methods, but some take time to run. This, of course, freezes my UI. Normally, I would call slow methods from a separate thread, but in this case I can't do that because the Context
object is tied to the application's UI thread.
Here's an example implementation of my Plugin class:
public class Plugin
{
public void Run(Context context)
{
// Create a window with my custom user interface
var window = new MyWindow();
// Call a slow method when MyButton is clicked
window.MyButton.Click += (o, e) => context.SlowMethod();
// Prevent exiting this Run method until the window is closed
window.ShowDialog();
}
}
What are some possible solutions that would allow me to call slow methods in the Context
object (bound to the UI thread) while having a responsive UI?
One solution I've come up with is to create a second thread on which to run my UI and use the original thread to call Context
methods. However, my implementation is rather complex, so I'd like to know if there's a more straightforward way to achieve this.
Here's my current solution:
public class Plugin
{
public void Run(Context context)
{
// Get the application's UI thread dispatcher
var dispatcher = Dispatcher.CurrentDispatcher;
// Create a dispatcher frame to push later
var frame = new DispatcherFrame();
// Create a new UI thread (using an StaTaskScheduler)
Task.Factory.StartNew(async () =>
{
var window = new MyWindow();
// The Click event handler now uses the original
// thread's dispatcher to run the slow method
window.MyButton.Click += async (o, e) =>
await dispatcher.InvokeAsync(() => context.SlowMethod());
window.ShowDialog();
// When the window is closed, end the dispatcher frame
frame.Continue = false;
}, CancellationToken.None, TaskCreationOptions.None, new StaTaskScheduler(1));
// Prevent exiting this Run method until the frame is done
Dispatcher.PushFrame(frame);
}
}
The StaTaskScheduler
schedules tasks in an STA thread (as required by WPF). It comes from the Parallel Extensions Extras library.
来源:https://stackoverflow.com/questions/56794412/whats-the-best-way-to-create-a-new-ui-thread-and-call-back-methods-on-the-origi