4

MVP パターンを使用して、アプリのスパゲッティ コードをリファクタリングしようとしています。しかし今、私はこれに苦労しています:

長い操作である DoWork メソッド (backgroundworker の) を呼び出すボタンを持つフォーム。私の質問は、長い操作をビューからプレゼンターに移動した場合、この操作からビューに進行状況の変更を送信するにはどうすればよいですか? BGW はプレゼンターにもある必要がありますか? これを行う方法のサンプルを教えてもらえますか?

前もって感謝します。

4

3 に答える 3

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ビューには、ビューの作成やイベント ハンドラーでの描画など、ビュー自体に非常に固有のコードのみが含まれます。ビュー内の別の種類のコードは、プレゼンターまたはコントローラーと通信するために必要なコードです。ただし、プレゼンテーション ロジックはプレゼンターにある必要があります。

BackgroundWorker_ProgressChangedUI スレッドで実行されるメソッドを使用して、ビューに変更を送信します。ビューのパブリック メソッドを呼び出すか、ビューのパブリック プロパティを設定するか、ビューのプロパティまたはコントロールのプロパティをバインドしてビューがアタッチできるパブリック プロパティを公開します。(これは MVVM パターンから借用したものです。)INotifyPropertyChangedビューをプレゼンターのプロパティにバインドする場合、プロパティが変更されたことをビューに通知するためにプレゼンターを実装する必要があります。

: UI スレッド以外のスレッドは、ビューを直接操作することはできません (そうしようとすると、例外がスローされます)。したがって、BackgroundWorker_DoWork はビューと直接やり取りできないため、ReportProgress を呼び出します。ReportProgress は、UI スレッドで BackgroundWorker_ProgressChanged を実行します。

于 2012-10-07T01:02:07.403 に答える
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 を Dispose することを忘れないでください。

于 2012-10-07T09:44:59.843 に答える
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();
    }
}
于 2012-10-07T18:50:10.663 に答える