web-dev-qa-db-ja.com

WinFormsのModel-View-Presenter

WinFormsを使用して、MVPメソッドを初めて実装しようとしています。

各層の機能を理解しようとしています。

私のプログラムには、クリックするとopenfiledialogウィンドウを開くGUIボタンがあります。

したがって、MVPを使用して、GUIはボタンクリックイベントを処理してからpresenter.openfile()を呼び出します。

Presenter.openfile()内で、そのファイルのオープンをモデルレイヤーに委任する必要がありますか、または処理するデータやロジックがないので、単にリクエストに応じてopenfiledialogウィンドウを開きますか?

更新:私はこれについてさらに支援が必要だと感じたため、賞金を提供することにしました。

さて、MVPを読んだ後、パッシブビューを実装することにしました。事実上、プレゼンターとモデルに委任されたタスクによって処理されるWinform上のコントロールの束があります。私の具体的なポイントは以下のとおりです。

  1. Winformがロードされると、ツリービューを取得する必要があります。したがって、ビューはpresenter.gettree()などのメソッドを呼び出す必要があると考えていますが、これはモデルに委任され、ツリービューのデータを取得し、作成して構成し、それを返しますプレゼンターは、ビューに渡され、ビューに単純にパネルなどに割り当てられますか?

  2. 私はデータグリッドビューも持っているので、これはWinformのデータコントロールでも同じでしょうか?

  3. 私のアプリには、同じアセンブリを持つ多くのモデルクラスがあります。また、起動時にロードする必要があるプラグインを持つプラグインアーキテクチャもサポートしています。ビューはプレゼンターメソッドを呼び出すだけで、プラグインをロードし、ビューに情報を表示するメソッドを呼び出しますか?その後、どの層がプラグイン参照を制御します。ビューはそれらまたはプレゼンターへの参照を保持しますか?

  4. ビューは、ツリービューのノードの色からデータグリッドのサイズなど、プレゼンテーションに関するすべてのことを処理するべきだと考えるのは正しいですか?

私はそれらが私の主な関心事であり、これらの流れがどうあるべきかを理解すれば大丈夫だと思う.

87
Darren Young

これは、MVPとあなたの特定の問題に対する私の謙虚な見解です。

最初、ユーザーが操作できる、または表示されるものはすべて、viewです。このようなビューの法則、動作、および特性は、interfaceによって記述されます。そのインターフェイスは、WinForms UI、コンソールUI、Web UI、またはUIをまったく使用せずに実装できます(通常はプレゼンターをテストする場合)。具体的な実装は、そのビューインターフェイスの法則に従う限り重要ではありません。 。

Second、ビューは常にpresenterによって制御されます。このようなプレゼンターの法則、動作、および特性は、interfaceによっても説明されます。このインターフェイスは、ビューインターフェイスの法則に従う限り、具体的なビューの実装には関心がありません。

Third、プレゼンターはビューを制御するため、依存関係を最小限に抑えるために、ビューがプレゼンターに関する情報をまったく把握していなくてもまったく利益はありません。プレゼンターとビューの間には合意された契約があり、それはビューインターフェースによって示されます。

Thirdの意味:

  • プレゼンターにはビューが呼び出すことのできるメソッドはありませんが、ビューにはプレゼンターがサブスクライブできるイベントがあります。
  • 発表者はその見解を知っています。具体的なプレゼンターでのコンストラクター注入を使用してこれを達成することを好みます。
  • ビューには、どのプレゼンターがそれを制御しているかはわかりません。プレゼンターには提供されません。

あなたの問題については、上記はやや単純化されたコードでは次のようになります。

_interface IConfigurationView
{
    event EventHandler SelectConfigurationFile;

    void SetConfigurationFile(string fullPath);
    void Show();
}

class ConfigurationView : IConfigurationView
{
    Form form;
    Button selectConfigurationFileButton;
    Label fullPathLabel;

    public event EventHandler SelectConfigurationFile;

    public ConfigurationView()
    {
        // UI initialization.

        this.selectConfigurationFileButton.Click += delegate
        {
            var Handler = this.SelectConfigurationFile;

            if (Handler != null)
            {
                Handler(this, EventArgs.Empty);
            }
        };
    }

    public void SetConfigurationFile(string fullPath)
    {
        this.fullPathLabel.Text = fullPath;
    }

    public void Show()
    {
        this.form.ShowDialog();        
    }
}

interface IConfigurationPresenter
{
    void ShowView();
}

class ConfigurationPresenter : IConfigurationPresenter
{
    Configuration configuration = new Configuration();
    IConfigurationView view;

    public ConfigurationPresenter(IConfigurationView view)
    {
        this.view = view;            
        this.view.SelectConfigurationFile += delegate
        {
            // The ISelectFilePresenter and ISelectFileView behaviors
            // are implicit here, but in a WinForms case, a call to
            // OpenFileDialog wouldn't be too far fetched...
            var selectFilePresenter = Gimme.The<ISelectFilePresenter>();
            selectFilePresenter.ShowView();
            this.configuration.FullPath = selectFilePresenter.FullPath;
            this.view.SetConfigurationFile(this.configuration.FullPath);
        };
    }

    public void ShowView()
    {
        this.view.SetConfigurationFile(this.configuration.FullPath);
        this.view.Show();
    }
}
_

上記に加えて、通常、Show()と私のビューが通常恩恵を受ける所有者ビューまたはビュータイトルを格納するベースIViewインターフェイスを持っています。

ご質問へ:

1。winformがロードされると、ツリービューを取得する必要があります。したがって、ビューはpresenter.gettree()などのメソッドを呼び出す必要があると考えていますが、これはモデルに委任され、ツリービューのデータを取得し、作成して構成し、それを返しますプレゼンターは、ビューに渡され、ビューに単純にパネルなどに割り当てられますか?

IConfigurationView.SetTreeData(...)を呼び出す直前に、IConfigurationPresenter.ShowView()からIConfigurationView.Show()を呼び出します

2。これはWingrid上のデータコントロールでも同じですが、datagridviewもありますか?

はい、そのためにIConfigurationView.SetTableData(...)を呼び出します。与えられたデータをフォーマットするのはビュー次第です。プレゼンターは、表形式のデータを必要とするビューの契約に従うだけです。

私のアプリには、同じアセンブリを持つ多くのモデルクラスがあります。また、起動時にロードする必要があるプラグインを持つプラグインアーキテクチャもサポートしています。ビューはプレゼンターメソッドを呼び出すだけで、プラグインをロードし、ビューに情報を表示するメソッドを呼び出しますか?その後、どの層がプラグイン参照を制御します。ビューはそれらまたはプレゼンターへの参照を保持しますか?

プラグインがビューに関連している場合、ビューはプレゼンターではなく、それらについて知っている必要があります。それらがすべてデータとモデルに関するものである場合、ビューはそれらとは何の関係もないはずです。

4。ビューは、ツリービューのノードの色からデータグリッドのサイズなど、プレゼンテーションに関するすべてのことを処理するべきだと思いますか?

はい。データを記述するXMLと、データを取得してCSSスタイルシートを適用するビューを提供するプレゼンターと考えてください。具体的には、プレゼンターはIRoadMapView.SetRoadCondition(RoadCondition.Slippery)を呼び出し、ビューは道路を赤色でレンダリングします。

クリックされたノードのデータはどうですか?

5。ツリーノードをクリックしたときに、特定のノードをプレゼンターに渡してから、プレゼンターが必要なデータを算出し、次に、ビューに表示する前に、モデルにそのデータを要求しますか?

可能であれば、ツリーを1つのビューで表示するために必要なすべてのデータを渡します。しかし、最初から渡すには大きすぎるデータがある場合、またはその性質が動的であり、モデルからの「最新のスナップショット」が必要な場合(プレゼンター経由)、ビューに_event LoadNodeDetailsEventHandler LoadNodeDetails_のようなものを追加しますインターフェイスは、プレゼンターがサブスクライブできるように、モデルから_LoadNodeDetailsEventArgs.Node_のノードの詳細を(おそらく何らかのIDを介して)フェッチし、イベントハンドラーが表示されたときにノードの詳細を表示できるようにしますデリゲートが戻ります。データのフェッチが優れたユーザーエクスペリエンスには遅すぎる場合は、この非同期パターンが必要になる場合があることに注意してください。

119
Johann Gerell

ビュー内のすべてのlogicを含むプレゼンターは、クリックされたボタンに@JochemKempe says として応答する必要があります。実際には、ボタンクリックイベントハンドラーはpresenter.OpenFile()を呼び出します。その後、プレゼンターは何をすべきかを決定できます。

ユーザーがファイルを選択する必要があると判断した場合、ビューにコールバックし(ビューインターフェイス経由)、すべてのUIを含むビューを許可します技術、OpenFileDialogを表示します。これは、使用中のUIテクノロジーに関連する操作をプレゼンターに許可しないという点で、非常に重要な違いです。

選択したファイルはプレゼンターに返され、プレゼンターはロジックを継続します。これには、ファイルの処理を処理するモデルまたはサービスが含まれます。

MVPパターンを使用する主な理由は、imoはUIテクノロジーをビューロジックから分離するためです。したがって、プレゼンターはすべてのロジックを調整しますが、ビューはUIロジックから切り離されたままにします。これには、プレゼンターを完全にユニットテスト可能にするという非常に素晴らしい副作用があります。

Update:プレゼンターは、特定のビューの1つの特定のビュー、ビュー-プレゼンターの関係は、IMOの1対1の関係です。そして、すべての実用的な目的のために、1つのビューインスタンス(フォームなど)は1つのプレゼンターインスタンスと対話し、1つのプレゼンターインスタンスは1つのビューインスタンスのみと対話します。

つまり、WinFormsでのMVPの実装では、プレゼンターは常に、ビューのUI機能を表すインターフェイスを介してビューと対話します。このインターフェイスを実装するビューに制限はないため、異なる「ウィジェット」が同じビューインターフェイスを実装し、プレゼンタークラスを再利用する場合があります。

10
Peter Lillevold

プレゼンターはリクエストに応じてアクションを実行する必要があります。モデルからのデータは必要ないため、プレゼンターはリクエストを処理できます。

モデルにエンティティを作成するためにデータが必要であると仮定しましょう。ストリームからエンティティを作成するメソッドがあるアクセスレイヤーにストリームトラフを渡すことができますが、プレゼンターでファイルの解析を処理し、モデルのエンティティごとにコンストラクターまたはCreateメソッドを使用することをお勧めします。

2
JochemKempe