wpf在ShowDialog上的异常,用于与后台工作者的进度对话框

本文关键字:工作者 后台 对话框 ShowDialog 异常 wpf 用于 | 更新日期: 2023-09-27 17:49:51

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

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

    }
  }
}

wpf在ShowDialog上的异常,用于与后台工作者的进度对话框

我建议不要在外部启动次要线程,而是在对话框本身的Loaded事件处理程序中启动。这样,您只需调用ShowDialog,其余的就会自行处理了。

找到问题了。之前我好像跳过了第一个例外。不管怎样,第一个异常有一个innerexception,它给出了实际的细节。在某个地方,我仍然从错误的线程修改我的可观察集合。