【问题标题】:using backgroundworker in Winforms (C#) with MVP pattern在具有 MVP 模式的 Winforms (C#) 中使用 backgroundworker
【发布时间】:2012-10-07 00:22:15
【问题描述】:

我一直在尝试使用 MVP 模式重构应用程序的意大利面条式代码。但现在我正在为此苦苦挣扎:

具有按钮的表单调用(后台工作人员的)DoWork 方法,这是一个长操作。我的问题是,如果我将长操作从视图移到 Presenter 中,那么如何将进度更改从此操作发送到视图? BGW也必须在Presenter中? 你能给我一个如何做到这一点的样本吗?

提前谢谢你。

【问题讨论】:

    标签: c# winforms backgroundworker mvp


    【解决方案1】:

    这概述了 BackgroundWorker 的使用:

    private BackgroundWorker _backgroundWorker;
    
    public void Setup( )
    {
        _backgroundWorker = new BackgroundWorker();
        _backgroundWorker.WorkerReportsProgress = true;
        _backgroundWorker.DoWork +=
          new DoWorkEventHandler(BackgroundWorker_DoWork);
        _backgroundWorker.ProgressChanged +=
          new ProgressChangedEventHandler(BackgroundWorker_ProgressChanged);
        _backgroundWorker.RunWorkerCompleted +=
          new RunWorkerCompletedEventHandler(BackgroundWorker_RunWorkerCompleted);
    
        // Start the BackgroundWorker
        _backgroundWorker.RunWorkerAsync();
    }
    
    void BackgroundWorker_DoWork(object sender, DoWorkEventArgs e)
    {
        // This method runs in a background thread. Do not access the UI here!
        while (work not done) {
            // Do your background work here!
    
            // Send messages to the UI:
            _backgroundWorker.ReportProgress(percentage_done, user_state);
            // You don't need to calculate the percentage number if you don't
            // need it in BackgroundWorker_ProgressChanged.
        }
        // You can set e.Result = to some result;
    }
    
    void BackgroundWorker_ProgressChanged(object sender,
                                          ProgressChangedEventArgs e)
    {
        // This method runs in the UI thread and receives messages from the backgroud thread.
    
        // Report progress using the value e.ProgressPercentage and e.UserState
    }
    
    void BackgroundWorker_RunWorkerCompleted(object sender,
                                             RunWorkerCompletedEventArgs e)
    {
        // This method runs in the UI thread.
        // Work is finished! You can display the work done by using e.Result
    }
    

    更新

    此 BackgroundWorker 必须在原因的演示者中。 MVP、MVC 或 MVVM 等模式的想法是从视图中删除尽可能多的代码。视图只有非常特定于视图本身的代码,例如在Paint 事件处理程序中创建视图或绘图等。视图中的另一种代码是与演示者或控制器通信所必需的代码。然而,呈现逻辑必须在呈现器中。

    您将使用在 UI 线程中运行的 BackgroundWorker_ProgressChanged 方法将更改发送到视图。通过调用视图的公共方法或通过设置视图的公共属性或通过将视图的属性或其控件的属性绑定到它来公开视图可以附加的公共属性。 (这是从 MVVM 模式借用的。)如果您决定将视图绑定到演示者的属性,演示者必须实现 INotifyPropertyChanged 以便通知视图属性已更改。

    注意:不允许 UI 线程以外的其他线程直接与视图交互(如果尝试这样做会引发异常)。因此 BackgroundWorker_DoWork 不能直接与视图交互,因此会调用 ReportProgress,而后者又会在 UI 线程中运行 BackgroundWorker_ProgressChanged。

    【讨论】:

      【解决方案2】:

      您可以将 BackGroundWorker 放置在 Presenter 中,并在视图中添加一个方法来显示进度。 像这样的:

      //Add a method to your view interface to show progress if you need it.
      public interface IView
      {
           void ShowProgress(int progressPercentage);
      }
      //Implement method in the view.
      public class MyView : Form, IView
      {
          public MyView()
          {
              //Assume you have added a ProgressBar to the form in designer.
              InitializeComponent();
          }
      
          public void ShowProgress(int progressPercentage)
          {
              //Make it thread safe.
      
              if (progressBar1.InvokeRequired)
                  progressBar1.Invoke(new MethodInvoker(delegate { progressBar1.Value = progressPercentage; }));
              else
                  progressBar1.Value = progressPercentage;
          }
      }
      
      // In your presenter class create a BackgroundWorker and handle it's do work event and put your time consuming method there.
      public class MyPresenter
      {
          private BackgroundWorker _bw;
      
          public MyPresenter()
          {
              _bw = new BackgroundWorker();
              _bw.WorkerReportsProgress = true;
              _bw.DoWork += new DoWorkEventHandler(_bw_DoWork);
          }
      
          private void _bw_DoWork(object sender, DoWorkEventArgs e)
          {
              //Time consuming operation
              while (!finished)
              {
                  //Do the job
                  _bw.ReportProgress(jobProgressPercentage);
      
              }
          }
      
          public void StartTimeConsumingJob()
          {
              _bw.RunWorkerAsync();
          }
      }
      

      完成后不要忘记 Dispose BackgroundWorker。

      【讨论】:

      • 感谢您的回答。但我仍然想知道节目进度将如何链接到表格。通过您的示例,progressbar1 驻留在演示者中?
      • progressbar1 在可以是表单的视图上。我编辑了示例以使其更清晰。
      • presenter MyPresenter 应该包含对 viewerIView 引用。否则它将无法与查看器进行通信。使用接口而不是MyView,以便在不更改presenter 的情况下使用其他viewer 实现。请在此处查看 MVP 模式的具体实现:msdn.microsoft.com/en-us/library/ff649571.aspx
      • ...查看者也应该持有演示者的实例,具体的或接口的。
      【解决方案3】:

      根据您的意见,我设法解决了这个问题。请评论您使用此方法可能发现的任何缺陷:

      * 查看界面 *

      public interface IView
      {
         void ShowProgress( int progressPercentage);
      }
      

      * 查看(表单)*

      public partial class Form1 : Form, IView
          {
              MyPresenter p ;
      
              public Form1()
              {
                  InitializeComponent();
                  p = new MyPresenter(this);
              }
      
              private void button1_Click(object sender, EventArgs e)
              {
                  if (p.IsBusy())
                  {
                      return;
                  }
                  p.StartTimeConsumingJob();
              }
      
              public void ShowProgress(int progressPercentage)
              {
                  if (progressBar1.InvokeRequired)
                      progressBar1.Invoke(new MethodInvoker(delegate { progressBar1.Value = progressPercentage; }));
                  else
                      progressBar1.Value = progressPercentage;
              }
      
              private void button2_Click(object sender, EventArgs e)
              {
                  p.Cancel();
              }
      
          }
      

      * 演示者 *

      public class MyPresenter
      {
          private BackgroundWorker _bw;
          private IView _view;
      
          public MyPresenter(IView Iview)
          {
              _view = Iview;
              _bw = new BackgroundWorker();
              _bw.WorkerReportsProgress = true;
              _bw.WorkerSupportsCancellation = true;
              _bw.DoWork += new DoWorkEventHandler(_bw_DoWork);
              _bw.ProgressChanged += new ProgressChangedEventHandler(_bw_ProgressChanged);
              _bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(_bw_Completed);
          }
      
          public void StartTimeConsumingJob()
          {
              _bw.RunWorkerAsync();
          }
      
          private void _bw_DoWork(object sender, DoWorkEventArgs e)
          {
              //Time consuming operation Do the job
              Thread.Sleep(1000);
              _bw.ReportProgress(50);
              Thread.Sleep(2000);
              if(_bw.CancellationPending)
              {
                  e.Result = false;
              }
          }
      
          public bool IsBusy()
          {
              return _bw.IsBusy;
          }
      
          public void Cancel()
          {
              _bw.CancelAsync();
          }
      
          private void _bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
          {
              _view.ShowProgress(e.ProgressPercentage);
          }
      
          private void _bw_Completed(object sender, RunWorkerCompletedEventArgs e)
          {
              if((bool)e.Result)
              _view.ShowProgress(100);
              else
                  _view.ShowProgress(0);
      
              _bw.Dispose();
          }
      }
      

      【讨论】:

      • @Libas 唯一的问题是您的视图中不应该有对演示者的引用。一个合适的 MVP 框架会为你做这个绑定。它对你有用,但从技术上讲,你违反了 MVP 模式,因为 View 不应该知道 Presenter。演示者知道视图。它在另一个方向上不起作用。这就是为什么所有 MVP 图中的箭头都是从 Presenter 流向 View 的原因。
      猜你喜欢
      • 2017-08-13
      • 2010-11-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-11-11
      相关资源
      最近更新 更多