在Winforms(C#)中使用带有MVP模式的backgroundworker

时间:2012-10-07 00:22:16

标签: c# winforms backgroundworker mvp

我一直在尝试使用MVP模式重构应用程序的意大利面条代码。但现在我正在努力解决这个问题:

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

提前谢谢。

3 个答案:

答案 0 :(得分:4)

这概述了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。

答案 1 :(得分:2)

您可以将BackGroundWorker放置在演示者中,并向视图添加方法以显示进度。 像这样:

//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();
    }
}

完成后不要忘记处理BackgroundWorker。

答案 2 :(得分:1)

根据您的意见,我已成功解决了这个问题。请评论您使用此方法找到的任何缺陷:

*查看界面*

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