wpf ShowDialog上与backgroundworker进行对话的例外情况

时间:2014-03-12 03:16:41

标签: c# wpf backgroundworker

我按照我在网上看到的一个例子创建了一个进度对话框(下面的代码),在主线程中,我调用了

  ProgressDialog pd = new ProgressDialog("Loading File: ", VCDFileName);
  pd.Owner = Application.Current.MainWindow;
  pd.WindowStartupLocation = WindowStartupLocation.CenterOwner;
  ModuleHierarchyVM.TopLevelModules.Clear();


  VCDData TempVCDOutput = null;
  Action handler = delegate
  {
      VCDParser.ParseVCDFileForAllPorts(VCDFileName, this, pd.Worker, ModuleHierarchyVM.TopLevelModules, out TempVCDOutput);
  };

  pd.SetBGWorkerDelegate(handler);
  pd.ShowDialog();

我认为错误发生在传递给委托的函数中。我想我有两个例外(每个线程可能有一个?)第一个说,

TargetInvocationException未处理。调用目标引发了异常。

我认为这个异常是由UI线程引发的,因为有时在显示异常之前传递给委托的函数内部会出现断点,有时它们不会。

然后在击中f5一段时间后,经历了在后台完成的函数中的许多断点,

我最终回到UI线程和pd.ShowDialog()并获得此异常:

InvalidOperationException未处理。只能在隐藏的窗口上调用ShowDailog。

我放了一堆try catch块来尝试捕获异常,如果它发生在传递给委托的函数内部,但我没有抓住它。它似乎没有

进度对话框中的代码

  public partial class ProgressDialog : Window
  {
    BackgroundWorker _worker;
    public BackgroundWorker Worker
    {
      get { return _worker; }
    }


    public string MainText
    {
      get { return MainTextLabel.Text; }
      set { MainTextLabel.Text = value; }
    }

    public string SubText
    {
      get { return SubTextLabel.Text; }
      set { SubTextLabel.Text = value; }
    }



    public bool IsCancellingEnabled
    {
      get { return CancelButton.IsVisible; }
      set { CancelButton.Visibility = value ? Visibility.Visible : Visibility.Collapsed; }
    }


    private bool _Cancelled = false;
    public bool Cancelled
    {
      get { return _Cancelled; }
    }

    private Exception error = null;
    public Exception Error
    {
      get { return error; }
    }


    private object result = null;
    public object Result
    {
      get { return result; }
    }


    /// <summary>
    /// Represents the method that will handle the DoWork event from the backgroundowkrker
    /// </summary>
    private Action workerCallback;

    private object BackgroundWorkerArgument;


    public ProgressDialog(string MainText, string SubText)
      : this()
    {
      this.MainText = MainText;
      this.SubText = SubText;
    }


    public ProgressDialog()
    {
      InitializeComponent();

      this.Loaded += new RoutedEventHandler(ProgressDialog_Loaded);

      _worker = new BackgroundWorker();
      _worker.WorkerReportsProgress = true;
      _worker.WorkerSupportsCancellation = true;


      _worker.DoWork += new DoWorkEventHandler(_worker_DoWork);
      _worker.ProgressChanged += new ProgressChangedEventHandler(_worker_ProgressChanged);
      _worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(_worker_RunWorkerCompleted);
      Closing += new CancelEventHandler(ProgressDialog_Closing);
    }

    void ProgressDialog_Loaded(object sender, RoutedEventArgs e)
    {
      _worker.RunWorkerAsync(BackgroundWorkerArgument);
    }

    void ProgressDialog_Closing(object sender, CancelEventArgs e)
    {
      //if progress dialog is open
      if (DialogResult == null)
      {
        MessageBoxResult messageBoxResult = System.Windows.MessageBox.Show("Are you sure you wish to cancel?",
           "Confirmation", System.Windows.MessageBoxButton.YesNo);
        if (messageBoxResult == MessageBoxResult.Yes)
        {
          if (_worker.IsBusy)
          {
            //notifies the async thread that a cancellation has been requested.
            _worker.CancelAsync();
          }
          DialogResult = false;
        }
        else
        {

          e.Cancel = true;
        }
      }
      else
      {

        if (_worker.IsBusy)
        {
          //notifies the async thread that a cancellation has been requested.
          _worker.CancelAsync();
        }
      }

    }




    void _worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
      if (!Dispatcher.CheckAccess())
      {
        //run on UI thread
        RunWorkerCompletedEventHandler handler = _worker_RunWorkerCompleted;
        Dispatcher.Invoke(handler, null, DispatcherPriority.SystemIdle, new object[] { sender, e });
        return;
      }
      else
      {
        if (e.Error != null)
        {
          error = e.Error;
        }
        else if (!e.Cancelled)
        {
          //assign result if there was neither exception nor cancel
          result = e.Result;
        }
        ProgressBar.Value = ProgressBar.Maximum;
        CancelButton.IsEnabled = false;


        //set the dialog result, which closes the dialog
        if (DialogResult == null)
        {
          if (error == null && !e.Cancelled)
          {
            DialogResult = true;
          }
          else
          {
            DialogResult = false;
          }
        }
      }

    }

    void _worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
    {
      if (!Dispatcher.CheckAccess())
      {
        //run on UI thread
        ProgressChangedEventHandler handler = _worker_ProgressChanged;
        Dispatcher.Invoke(handler, null, DispatcherPriority.SystemIdle, new object[] { sender, e });
        return;
      }
      else
      {
        if (e.ProgressPercentage != int.MinValue)
        {
          ProgressBar.Value = e.ProgressPercentage;
        }

      }

    }

    void _worker_DoWork(object sender, DoWorkEventArgs e)
    {
      try
      {
        if ((_worker.CancellationPending == true))
        {
          //cancel the do work event
          e.Cancel = true;
        }
        else
        {
          // Perform a time consuming operation and report progress.
          workerCallback();
        }

      }
      catch (Exception)
      {
        //disable cancelling and rethrow the exception
        Dispatcher.BeginInvoke(DispatcherPriority.Normal,
          new Action(delegate { CancelButton.SetValue(Button.IsEnabledProperty, false); }), null);
        throw;
      }
    }




    public void SetBGWorkerDelegate(Action workHandler)
    {
      SetBGWorkerDelegate(null, workHandler);
    }
    public void SetBGWorkerDelegate(object argument, Action workHandler)
    {
      //store reference to callback handler and launch worker thread
      workerCallback = workHandler;
      BackgroundWorkerArgument = argument;



    }


    private void CancelButton_Click(object sender, RoutedEventArgs e)
    {
      MessageBoxResult messageBoxResult = System.Windows.MessageBox.Show("Are you sure you wish to cancel?", 
        "Confirmation", System.Windows.MessageBoxButton.YesNo);
      if (messageBoxResult == MessageBoxResult.Yes)
      {
        if (_worker.IsBusy)
        {
          //notifies the async thread that a cancellation has been requested.
          _worker.CancelAsync();
        }
        DialogResult = false;        
      }


    }
  }
}

2 个答案:

答案 0 :(得分:1)

我建议不要在外部启动辅助线程,而是在对话本身的Loaded事件处理程序中启动。这样你就可以简单地调用ShowDialog,其余的则自行处理。

答案 1 :(得分:0)

发现我的问题。早些时候我想我以某种方式跳过了第一个例外。无论如何,第一个例外有一个不确定的例子,它给出了实际的细节。在某个地方,我仍在从错误的线程修改我的可观察集合。我以为我已经改变了所有这些,但仍然必须有一个