3

我在一个非 UI 线程上,我需要创建并显示一个 SaveDialog。但是当我尝试显示它时: .ShowDialog() 我得到:

“System.Windows.Forms.dll 中发生了‘System.Threading.ThreadStateException’类型的未处理异常”

我没有在 UI 上创建对象,那么如何在 UI 线程中调用此 SaveDialog?是否有我可以使用的全局 UI SynchronizationContext?

编辑:如果我有一个表单/控件对象来调用调用/开始调用,我知道该怎么做。或者在 UI 线程中使用 SynchronizationContext.Current 引用。但我没有这些。

4

2 回答 2

3

在 .NET 中,您将使用SynchronizationContext该类并将其实例传递给您的方法,可能有点像这样。

public void DoSomeStuffOnBackgroundThread(SynchronizationContext synchronizationContext)
{
    // Do some stuff here
    // ...

    // Show the dialog on the UI thread
    var dialog = new SaveFileDialog();
    synchronizationContext.Send(() => dialog.Show());

    // Send is performed synchronously, thus this line of code only executes when the dialog was closed. You can extract the file name here
    var fileName = dialog.FileName;
}

同步上下文是在 windows 窗体框架启动时构建的。因此,当您的应用程序和 UI 消息循环启动时,只需调用SynchronizationContext.CurrentUI 线程即可获取可以传递给方法或对象的实例。

最后,我绝对建议将这个功能封装在一个接口后面,并且实现这个接口的一个类获得对同步上下文的引用。因此,您的生产代码不会被这些不必要的细节(如线程亲和性)污染。

如果您有任何疑问,请随时发表评论。

于 2013-06-17T12:56:58.847 回答
2

我处理此问题的方法是将 UI 线程传递TaskScheduler到将在后台线程上运行的对象中。然后,每当后台线程需要在 UI 线程上发生某些事情时,我都会使用Task.Factory.StartNew()TaskScheduler提供给对象的 。为了显示:

using System.Threading.Tasks;

public class BackgroundThread {
    IView _view;
    TaskScheduler _uiThreadScheduler;

    // This object is constructed on the main thread. The main thread's TaskScheduler
    // can be acquired with TaskScheduler.FromCurrentSynchronizationContext()
    public BackgroundThread(IView view, TaskScheduler uiThreadScheduler){
        this._view = view;
        this._uiThreadScheduler = uiThreadScheduler;
    }

    public void DoWork(){
        // Code in this function executes on a background thread
        //...
        string filename;
        var task = Task.Factory.StartNew(() =>
        {
            filename = _view.GetSaveFilename();
        }, CancellationToken.None, TaskCreationOptions.None, _uiThreadScheduler);

        task.Wait();

        // filename now has input from the user, or is null
    }

}

通过将 UI 的实现隐藏在IView接口后面,后台线程不依赖于任何特定的 UI 实现。

于 2013-06-17T18:47:24.170 回答