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

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

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

先感谢您。

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

UPDATE

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

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

注意 :不允许除UI线程之外的另一个线程直接与视图交互(如果尝试这样做,则抛出exception)。 因此,BackgroundWorker_DoWork无法直接与视图交互,因此调用ReportProgress,后者又在UI线程中运行BackgroundWorker_ProgressChanged。

您可以将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。

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

*查看界面*

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