1. ホーム
  2. c#

[解決済み] WinFormsのモデル-ビュー-プレゼンター

2023-02-11 15:23:17

質問

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

各レイヤーの機能を理解しようと思っています。

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

そこでMVPを使用して、GUIはボタンのクリックイベントを処理し、次にpresenter.openfile()を呼び出します。

Presenter.openfile() 内で、そのファイルのオープンをモデル層に委ねるべきか、または処理するデータやロジックがないため、単にリクエストに対処して openfiledialog ウィンドウを開くべきでしょうか?

更新しました。 私はこれに関してさらなる支援を必要とし、できれば私が文脈を持つように、以下の私の特定のポイントに合わせたと感じたので、私は報奨金を提供することを決定しました。

さて、MVPについて読みあさった後、私はパッシブビューを実装することを決定しました。 事実上、私はプレゼンターによって処理される Winform 上のコントロールの束を持っており、その後タスクはモデル (複数可) に委譲される予定です。 私の具体的なポイントは以下のとおりです。

  1. winformがロードされるとき、それはツリービューを取得する必要があります。 そのため、ビューは次のようなメソッドを呼び出すべきだと考えていますが、正しいでしょうか: presenter.gettree(), これは順番にモデルに委任し、モデルはツリービューのデータを取得し、それを作成して構成し、プレゼンターに返し、今度はビューに渡して、それを単に、たとえばパネルに割り当てることになります?

  2. 私はdatagridviewも持っているので、これはWinform上の任意のデータコントロールのために同じでしょうか?

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

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

これらは私の主な関心事であり、これらのためにフローがどのようにあるべきかを理解すれば、私は大丈夫だと思います。

どのように解決するのですか?

これは、MVPとあなたの具体的な問題についての私の謙虚な考えです。

最初に のように、ユーザーが操作できるもの、あるいは単に表示されるものは、すべて ビュー . このようなビューの法則、動作、特性は インターフェース . そのインターフェイスは WinForms UI、コンソール UI、Web UI、あるいはまったく UI を使用しないで実装することができます (通常、プレゼンターをテストする場合)。

2 番目 の場合、ビューは常に プレゼンター . このようなプレゼンターの法則、振る舞い、特性もまた インターフェース . そのインターフェイスは、そのビューインターフェイスの法則に従う限り、具体的なビューの実装には何の関心も持ちません。

第三に プレゼンターはビューを制御するので、依存関係を最小にするために、ビューがプレゼンターについて全く何も知らないということは、実際には何の利益もありません。プレゼンターとビューの間には合意された契約があり、それはビューインタフェースによって記述されています。

の意味するところは 第三の

  • プレゼンターはビューが呼び出すことのできるメソッドを持っていませんが、ビューはプレゼンターがサブスクライブできるイベントを持っています。
  • プレゼンターはそのビューを知っています。私は具象プレゼンターのコンストラクタ注入でこれを達成することを好みます。
  • ビューは、どのようなプレゼンターがそれを制御しているのかを知りません; それは単に任意のプレゼンターを提供されることはないでしょう。

あなたの問題に対して、上記はやや簡略化されたコードで次のようになります。

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

上記に加え、普段はベースとなる IView インターフェースを作成し、そこに Show() を格納し、ビューが通常恩恵を受けるオーナービューやビュータイトルを格納します。

あなたの質問に対して

1. winformがロードされるとき、それはツリービューを取得する必要があります。そのため、ビューは次のようなメソッドを呼び出すべきであると考えるのは正しいでしょうか: Presenter.gettree(), これは順番にモデルに委譲し、モデルはツリービューのデータを取得し、それを作成して構成し、プレゼンターにそれを返し、今度はビューに渡して、それを例えばパネルに単に割り当てます?

私なら IConfigurationView.SetTreeData(...) から IConfigurationPresenter.ShowView() への呼び出しの直前で IConfigurationView.Show()

2. datagridviewも持っているので、これはWinform上のどのデータコントロールでも同じでしょうか?

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

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

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

4. ツリービューのノードの色からデータグリッドのサイズなど、表示に関するあらゆることをビューが処理すべきであると考えるのは正しいですか?

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

クリックされたノードのデータについてはどうでしょうか?

5. もし私がtreenodesをクリックした場合、私は特定のノードをプレゼンターに渡すべきで、そこからプレゼンターはどのデータが必要かを調べ、ビューにそれを戻す前にそのデータをモデルに要求するのでしょうか?

可能であれば、ビューでツリーを表示するために必要なすべてのデータを一発で渡したいところです。しかし、一部のデータが大きすぎて最初から渡せない場合や、その性質が動的でモデルから (プレゼンターを介して) "最新のスナップショット" を必要とする場合、次のようなものを追加します。 event LoadNodeDetailsEventHandler LoadNodeDetails のようなものをビューインターフェースに追加し、プレゼンターがそれをサブスクライブし、ノードの詳細を LoadNodeDetailsEventArgs.Node (にあるノードの詳細をモデルから取得し、イベントハンドラーデリゲートが戻ったときにビューが表示されているノードの詳細を更新できるようにします。データをフェッチすることが良いユーザーエクスペリエンスのために遅すぎるかもしれない場合、この非同期パターンが必要かもしれないことに注意してください。