C# 在BackgroundWorker中调用ShowDialog

C# 在BackgroundWorker中调用ShowDialog,c#,winforms,backgroundworker,C#,Winforms,Backgroundworker,我有一个WinForms应用程序,我的后台工作人员在其中执行同步任务,添加新文件,删除旧文件等 在我的后台工作人员代码中,我想向用户显示一个自定义表单,告诉他如果继续,将删除什么和将添加什么,并使用“是/否”按钮获取他的反馈 我想知道在后台工作者的工作方法中这样做是否可以? 如果没有,我应该怎么做 请告知 private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) { MyForm f = new MyFo

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

在我的后台工作人员代码中,我想向用户显示一个自定义表单,告诉他如果继续,将删除什么和将添加什么,并使用“是/否”按钮获取他的反馈

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

请告知

private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
   MyForm f = new MyForm();
   f.FilesToAddDelete(..);
   DialogResult result = f.ShowDialog();
   if(No...)
   return;
   else
   //keep working...
}

如果您尝试这样做,您会发现它不起作用,因为
BackgroundWorker
线程不起作用(它来自)

问题的实质是,您无法从工作线程1显示用户界面,因此必须解决这个问题。您应该向应用程序的UI元素传递一个引用(主表单是一个不错的选择),然后使用将用户交互请求封送到UI线程。一个简单的例子:

class MainForm
{

    // all other members here

    public bool AskForConfirmation()
    {
        var confirmationForm = new ConfirmationForm();
        return confirmationForm.ShowDialog() == DialogResult.Yes;
    }
}
后台工作人员会这样做:

// I assume that mainForm has been passed somehow to BackgroundWorker
var result = (bool)mainForm.Invoke(mainForm.AskForConfirmation);
if (result) { ... }


从技术上讲,您不能从非STA线程显示用户界面。如果您自己创建一个工作线程,您可以选择将其设置为STA,但如果它来自线程池,则不存在这种可能性。

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

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

public void消费(ImageFound消息)
{
var模型=_container.Resolve();
model.ForImage(消息);
UIDispatcher.BeginInvoke(新操作(()=>_windows.ShowWindow(模型));
}
WinForms等价物 不要将UIDispatcher设置为任何值,那么您可以:

public void Consume(ImageFound message)
{
    var model = _container.Resolve<ChoiceViewModel>();
    model.ForImage(message);
    this.Invoke( () => _windows.ShowWindow(model) );
}
public void消费(ImageFound消息)
{
var模型=_container.Resolve();
model.ForImage(消息);
this.Invoke(()=>\u windows.ShowWindow(model));
}
为WPF干燥: 伙计,这么多代码

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);
    }
}
公共接口ThreadedViewModel
:i消费者
{
/// 
///获取UI线程调度程序
/// 
调度程序UIDispatcher{get;}
}
公共静态类ThreadedViewModelEx
{
public static void BeginInvoke([NotNull]此ThreadedViewModel viewModel[NotNull]操作)
{
如果(viewModel==null)抛出新的ArgumentNullException(“viewModel”);
如果(action==null)抛出新的ArgumentNullException(“action”);
if(viewModel.UIDispatcher.CheckAccess())action();
else viewModel.UIDispatcher.BeginInvoke(操作);
}
}
在视图模型中:

    public void Consume(ImageFound message)
    {
        var model = _container.Resolve<ChoiceViewModel>();
        model.ForImage(message);
        this.BeginInvoke(() => _windows.ShowWindow(model));
    }
public void消费(ImageFound消息)
{
var模型=_container.Resolve();
model.ForImage(消息);
this.BeginInvoke(()=>\u windows.ShowWindow(model));
}

希望有帮助。

在运行backgroundworker之前,您应该打开该对话框。在progresschanged事件中,您可以更新对话框。

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

  private void DoOnUIThread(MethodInvoker d) {
     if (this.InvokeRequired) { this.Invoke(d); } else { d(); }
  }
使用此选项,您可以将代码更改为:

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...
}

说明另一个线程应该启动的答案在哪里?我从来没有看到过这样的答案来回答我的问题。。可能是有人在互联网上创建和删除了它。我今天自己也有这个问题。:)谢谢Jon我会试试看是否对我有用。。你的回答很有道理。顺便说一下,有人在别处告诉我,在BackgroundWorker上调用MessageBox.show(..)是安全的,可能是因为该方法是statis。。这也不安全吗?@Ahmed:有人不知道他们在说什么。这里有一个相关的问题,请参见。结论:您可以使用它,但不是因为它是
静态的
或其他类似的垃圾原因。这是因为它专门设置了自己的消息泵。谢谢你,乔恩,你是真正的大师:)请查看我所指的关于MessageBox的帖子,在这里,它是静态的,没有指向消息泵@艾哈迈德:嗯,这个答案的措辞确实不是最准确的。我想这是因为写这本书的人(毫无疑问,他知道这本书为什么有效)和你之间的沟通不完善。
  private void DoOnUIThread(MethodInvoker d) {
     if (this.InvokeRequired) { this.Invoke(d); } else { d(); }
  }
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...
}