我正在为第三方GUI应用程序编写一个插件类。应用程序调用了我的类的Run
方法,并向我传递了供应商定义的Context
对象。此Context
对象中的任何方法或属性必须从当前线程(即应用程序的UI线程)中调用。
我的插件代码创建WPF Window
并将其显示给用户。有些交互需要调用Context
对象的方法,但是有些交互需要花费一些时间才能运行。当然,这会冻结我的UI。通常,我会从单独的线程中调用慢速方法,但是在这种情况下,由于Context
对象与应用程序的UI线程相关联,因此我无法做到这一点。
这是我的Plugin类的示例实现:
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();
}
}
有哪些可能的解决方案使我可以在具有响应式UI的情况下在Context
对象(绑定到UI线程)中调用慢速方法?
我想出的一个解决方案是创建第二个线程,在该线程上运行我的UI,并使用原始线程调用Context
方法。但是,我的实现非常复杂,因此我想知道是否有更简单的方法来实现这一目标。
这是我当前的解决方案:
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);
}
}
StaTaskScheduler
在STA线程中调度任务(根据WPF的要求)。它来自Parallel Extensions Extras库。