首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >在BackgroundWorker中调用ShowDialog

在BackgroundWorker中调用ShowDialog
EN

Stack Overflow用户
提问于 2012-05-08 20:23:59
回答 4查看 23.6K关注 0票数 7

我有一个WinForms应用程序,后台工作人员正在执行同步任务,添加新文件,删除旧文件等。

在我的后台worker代码中,我想向用户显示一个自定义表单,告诉他如果继续,将删除什么和添加什么,并使用YES/NO按钮获取反馈。

我想知道在后台工作者的doWork方法中做这样的事情是否合适?如果没有,我应该怎么做?

请指教..

代码语言:javascript
复制
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
   MyForm f = new MyForm();
   f.FilesToAddDelete(..);
   DialogResult result = f.ShowDialog();
   if(No...)
   return;
   else
   //keep working...
}
EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2012-05-08 20:26:20

如果您尝试这样做,您会自己看到它不会工作,因为BackgroundWorker线程不是STA (它来自managed thread pool)。

问题的本质是你不能从工作线程显示用户界面,所以你必须解决这个问题。您应该传递对应用程序的UI元素的引用(主窗体将是一个很好的选择),然后使用Invoke将用户交互请求编组到您的UI线程。一个简单的例子:

代码语言:javascript
复制
class MainForm
{

    // all other members here

    public bool AskForConfirmation()
    {
        var confirmationForm = new ConfirmationForm();
        return confirmationForm.ShowDialog() == DialogResult.Yes;
    }
}

后台工作人员会这样做:

代码语言:javascript
复制
// I assume that mainForm has been passed somehow to BackgroundWorker
var result = (bool)mainForm.Invoke(mainForm.AskForConfirmation);
if (result) { ... }

?从技术上讲,您不能从非STA的线程显示用户界面。如果你自己创建一个工作线程,你可以选择让它成为STA,但如果它来自线程池,就没有这种可能性。

票数 8
EN

Stack Overflow用户

发布于 2012-05-08 20:38:48

我通常会创建一个方法来在UI线程上执行委托:

代码语言:javascript
复制
  private void DoOnUIThread(MethodInvoker d) {
     if (this.InvokeRequired) { this.Invoke(d); } else { d(); }
  }

这样,您可以将代码更改为:

代码语言:javascript
复制
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
   DialogResult result = DialogResult.No;
   DoOnUIThread(delegate() {
      MyForm f = new MyForm();
      f.FilesToAddDelete(..);
      result = f.ShowDialog();
   });

   if(No...)
   return;
   else
   //keep working...
}
票数 4
EN

Stack Overflow用户

发布于 2012-05-08 20:31:47

IMO回答说,您应该启动一个线程来处理此问题,这是错误的。您需要的是将窗口跳回到主调度程序线程。

在WPF中

代码语言:javascript
复制
public ShellViewModel(
    [NotNull] IWindowManager windows, 
    [NotNull] IWindsorContainer container)
{
    if (windows == null) throw new ArgumentNullException("windows");
    if (container == null) throw new ArgumentNullException("container");
    _windows = windows;
    _container = container;
    UIDispatcher = Dispatcher.CurrentDispatcher; // not for WinForms
}

public Dispatcher UIDispatcher { get; private set; }

然后,当某个事件在另一个线程(本例中为线程池线程)上发生时:

代码语言:javascript
复制
public void Consume(ImageFound message)
{
    var model = _container.Resolve<ChoiceViewModel>();
    model.ForImage(message);
    UIDispatcher.BeginInvoke(new Action(() => _windows.ShowWindow(model)));
}

WinForms等效项

不要将UIDispatcher设置为任何值,这样您就可以拥有:

代码语言:javascript
复制
public void Consume(ImageFound message)
{
    var model = _container.Resolve<ChoiceViewModel>();
    model.ForImage(message);
    this.Invoke( () => _windows.ShowWindow(model) );
}

DRYing it up for WPF:

伙计,这么多代码...

代码语言:javascript
复制
public interface ThreadedViewModel
    : IConsumer
{
    /// <summary>
    /// Gets the UI-thread dispatcher
    /// </summary>
    Dispatcher UIDispatcher { get; }
}

public static class ThreadedViewModelEx
{
    public static void BeginInvoke([NotNull] this ThreadedViewModel viewModel, [NotNull] Action action)
    {
        if (viewModel == null) throw new ArgumentNullException("viewModel");
        if (action == null) throw new ArgumentNullException("action");
        if (viewModel.UIDispatcher.CheckAccess()) action();
        else viewModel.UIDispatcher.BeginInvoke(action);
    }
}

在视图模型中:

代码语言:javascript
复制
    public void Consume(ImageFound message)
    {
        var model = _container.Resolve<ChoiceViewModel>();
        model.ForImage(message);
        this.BeginInvoke(() => _windows.ShowWindow(model));
    }

希望能有所帮助。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/10498555

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档