使用Winforms(C#)中的backgroundworker和MVP模式
我一直在尝试使用MVP模式重构应用程序的意大利面代码。但现在我正在努力解决这个问题: 一个窗体,它有一个调用DoWork方法(backgroundworker的)的按钮,这是一个很长的操作。我的问题是,如果我将长操作从视图移到演示者中,那么如何将此操作的进度更改发送到视图?BGW也必须在演示者中? 你能给我一个怎样做的例子吗使用Winforms(C#)中的backgroundworker和MVP模式,c#,winforms,backgroundworker,mvp,C#,Winforms,Backgroundworker,Mvp,我一直在尝试使用MVP模式重构应用程序的意大利面代码。但现在我正在努力解决这个问题: 一个窗体,它有一个调用DoWork方法(backgroundworker的)的按钮,这是一个很长的操作。我的问题是,如果我将长操作从视图移到演示者中,那么如何将此操作的进度更改发送到视图?BGW也必须在演示者中? 你能给我一个怎样做的例子吗 提前感谢。这概述了BackgroundWorker的使用: private BackgroundWorker _backgroundWorker; public void
提前感谢。这概述了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
}
更新 这个幕后工作者必须是原因的陈述者。MVP、MVC或MVVM等模式的想法是从视图中删除尽可能多的代码。视图只有非常特定于视图本身的代码,比如在
Paint
事件处理程序中创建视图或图形等等。视图中的另一种代码是与演示者或控制器通信所需的代码。但是,演示逻辑必须在演示者中
您可以使用UI线程中运行的BackgroundWorker\u ProgressChanged
方法将更改发送到视图。通过调用视图的公共方法或设置视图的公共属性,或通过将视图的属性或其控件的属性绑定到视图来公开视图可以附加到的公共属性。(这是从MVVM模式借用的。)如果您决定将视图绑定到演示者的属性,演示者必须实现INotifyPropertyChanged
,以便通知视图属性已更改
注意:不允许UI线程以外的其他线程直接与视图交互(如果尝试这样做,将引发异常)。因此,BackgroundWorker\u DoWork无法直接与视图交互,因此调用ReportProgress,后者在UI线程中运行BackgroundWorker\u 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();
}
}
*查看(表格)*
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();
}
}
*演示者*
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();
}
}
谢谢你的回答。但我仍然想知道节目进展如何与表格联系起来。例如,progressbar1位于presenter?progressbar1位于视图上,该视图可以是表单。我编辑了示例以使其更清晰。演示者
MyPresenter
应该包含对查看者的IView
引用。否则它将无法与查看器通信。使用界面而不是MyView
,这样就可以在不更改演示者的情况下使用其他查看器实现。请在此处查看MVP模式的具体实现:…并且查看器也应该包含演示者的实例,具体的或接口的。@Libas唯一的问题是您不应该在视图中引用演示者。一个合适的MVP框架可以为您实现这一绑定。它对您有效,但从技术上讲,您违反了MVP模式,因为视图不应该知道演示者。演示者知道该视图。它在另一个方向不起作用。这就是为什么所有MVP图表中的箭头都从演示者流向视图。