我一直在尝试使用 MVP 模式重构应用程序的意大利面条代码。但现在我正在为此苦苦挣扎:
具有按钮的表单,该按钮调用(后台工作人员的)DoWork 方法,这是一个长操作。我的问题是,如果我将长操作从视图移到 Presenter 中,那么如何将进度更改从此操作发送到视图?BGW也必须在Presenter中?你能给我一个如何做到这一点的样本吗?
先感谢您。
我一直在尝试使用 MVP 模式重构应用程序的意大利面条代码。但现在我正在为此苦苦挣扎:
具有按钮的表单,该按钮调用(后台工作人员的)DoWork 方法,这是一个长操作。我的问题是,如果我将长操作从视图移到 Presenter 中,那么如何将进度更改从此操作发送到视图?BGW也必须在Presenter中?你能给我一个如何做到这一点的样本吗?
先感谢您。
这概述了 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
事件处理程序中创建视图或绘图等。视图中的另一种代码是与演示者或控制器通信所必需的代码。然而,呈现逻辑必须在呈现器中。
您将使用BackgroundWorker_ProgressChanged
在 UI 线程中运行的方法将更改发送到视图。通过调用视图的公共方法或通过设置视图的公共属性或通过将视图的属性或其控件的属性绑定到它来公开视图可以附加的公共属性。(这是从 MVVM 模式借用的。)INotifyPropertyChanged
如果您决定将视图绑定到演示者的属性,演示者必须实现以通知视图属性已更改。
注意:UI 线程以外的其他线程不允许直接与视图交互(如果尝试这样做会引发异常)。因此,BackgroundWorker_DoWork 不能直接与视图交互,因此会调用 ReportProgress,而后者又会在 UI 线程中运行 BackgroundWorker_ProgressChanged。
您可以将 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。
根据您的意见,我设法解决了这个问题。请评论您使用此方法可能发现的任何缺陷:
* 查看界面 *
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();
}
}