0

5 つを超えるビューとビューモデルを持つ中間 MVVM アプリケーションがある場合、そのようなアプリケーションのスキャフォールディングを行う方法について推奨される設計パターンはありますか?

現在、私は通常、 App.OnStartup で作成されたコントローラーを持っています。

  • メイン ビューを設定します
  • サブビューを挿入します(通常、「内部ウィンドウ」を持つステータスバーとナビゲーションを備えたMainWindowがあります)
  • ビューとビューモデルの結合を処理します。
  • ナビゲーションを処理します (ビュー A からビュー B に移動します)
  • ブレッドクラム ナビゲーション (および典型的な NavigationService.GoBack() など) をサポートします。

優れた設計パターンはすでにあると思いますが、私が聞いたり読んだりしたものはありません。

質問は次のとおりです。ビューモデルとビューの結合(データコンテキストの設定)とビュー間のナビゲーションを処理する方法について、一般的に受け入れられているパターンはありますか?

私の意見では、view-first (XAML で DataContext を設定する) と ViewModel-First (viewmodel に DI/IOC を介して注入されたビューを取得させる) は、どちらもビューとビューモデルの間に依存関係があるため、あまり良くありません。

プレーンな MVVM は、MVVM マシン全体のセットアップ方法を想定していません。この非常に一般的な問題には、「既成の」解決策がないのではないかと思っています。コントローラーは広く使用されていると思います。他の人はそれをどのように解決しますか?

4

3 に答える 3

1

View と ViewModel の間の「リンク」に関して、この記事の の概念はDataTemplateManager非常興味深いものでした。基本的に、次のようなことができます

DataTemplateManager.Register<TViewModel1,TView1>();
DataTemplateManager.Register<TViewModel2,TView2>();
DataTemplateManager.Register<TViewModel3,TView3>();

確かに、これは最善の解決策ではないかもしれませんが、非常に便利です。私はすでにそれを自作の MVVM フレームワークに組み込んでいます。

于 2013-03-28T19:12:19.483 に答える
1

ViewFinderと呼ばれるいくつかの静的メソッドを持つと と呼ばれるシングルトン クラスを含む小規模なプロジェクトがMakeWindowFor(vm)ありMakeDialogFor(vm)、どちらもビューモデルをパラメーターとして受け取ります。ViewFinderビューモデルを、それらに対応するように設定したウィンドウにリンクする辞書があります。おそらくビューは単なるウィンドウではなく、別のビューの中にあるため、さらに情報を追加できます。

これはタスクを達成するための最良の方法ではないかもしれませんが、このプロジェクトでの私のニーズには合っていて、実際のビューの実装をビューモデルに認識させません。私のすべてのビューモデルの祖先には、メッセージ ボックスの表示などのイベントが含まれており、すべてのウィンドウは、これらの共通イベントをサブスクライブして反応する方法を知っている基本クラスから派生しています。

public class ViewFinder {
    private static ViewFinder m_Instance;
    public static ViewFinder Instance {
        get {
            if (m_Instance == null)
                m_Instance = new ViewFinder();
            return (m_Instance);
        }
    }

    /// Maps viewmodels to windows/dialogs. The key is the type of the viewmodel, the value is the type of the window.
    private Dictionary<Type, Type> ViewDictionary = new Dictionary<Type, Type>();

    /// Private constructor because this is a singleton class.
    ///
    /// Registers the viewmodels/views.
    private ViewFinder() {
        Register(typeof(SomeViewModel), typeof(SomeWindowsForViewModel));
        Register(typeof(SomeViewModel2), typeof(SomeWindowsForViewModel2));
    }

    /// Registers a window with a viewmodel for later lookup.
    /// <param name="viewModelType">The Type of the viewmodel. Must descend from ViewModelBase.</param>
    /// <param name="windowType">The Type of the view. Must descend from WindowBase.</param>
    public void Register(Type viewModelType, Type windowType) {
        if (viewModelType == null)
            throw new ArgumentNullException("viewModelType");
        if (windowType == null)
            throw new ArgumentNullException("windowType");
        if (!viewModelType.IsSubclassOf(typeof(ViewModelBase)))
            throw new ArgumentException("viewModelType must derive from ViewModelBase.");
        if (!windowType.IsSubclassOf(typeof(WindowBase)))
            throw new ArgumentException("windowType must derive from WindowBase.");
        ViewDictionary.Add(viewModelType, windowType);
    }

    /// Finds the window registered for the viewmodel and shows it in a non-modal way.
    public void MakeWindowFor(ViewModelBase viewModel) {
        Window win = CreateWindow(viewModel);
        win.Show();
    }

    /// Finds a window for a viewmodel and shows it with ShowDialog().
    public bool? MakeDialogFor(ViewModelBase viewModel) {
        Window win = CreateWindow(viewModel);
        return (win.ShowDialog());
    }

    /// Helper function that searches through the ViewDictionary and finds a window. The window is not shown here,
    /// because it might be a regular non-modal window or a dialog.
    private Window CreateWindow(ViewModelBase viewModel) {
        Type viewType = ViewDictionary[viewModel.GetType()] as Type;
        if (viewType == null)
            throw new Exception(String.Format("ViewFinder can't find a view for type '{0}'.", viewModel.GetType().Name));
        Window win = Activator.CreateInstance(viewType) as Window;
        if (win == null)
            throw new Exception(String.Format("Activator returned null while trying to create instance of '{0}'.", viewType.Name));
        win.DataContext = viewModel;
        return win;
    }
}
于 2013-03-28T20:02:28.227 に答える
1

考慮すべき設計パターンには、制御の反転 (IoC)イベント アグリゲーターがあります。

C# / MVVM の場合、Caliburn Micro Framework (2 つのうちの 1 つ) を使用すると、IoC と Event Aggregator がはるかに簡単になります。

ViewModel を View から完全に分離する既製のソリューションがないという点で、MVVM の核となる問題を正しく特定しました。ViewModel は View と対になるように構築されているというのが中心的な概念です。問題は、ViewModel / View ペアリングのインスタンスを管理する方法に帰着します。

View first アプローチは、View が必要に応じて ViewModel を認識し、インスタンス化できることを前提としています。これは、どの View クラスにも複数の責任があるため、SoC の問題です。ViewModel のスピンアップと UI の処理。

最初にモデルを表示することは困難です。これは、MVVM の主要なテナントの 1 つを壊すことにつながることが多いためです。つまり、関連付けられたビューがなくても VM をテストできる必要があります。

これは、通常、IoC の出番です。通常、IoC はビュー レイヤーに存在します (これは、必要に応じてすべてのビューおよびビューモデル クラスにアクセスできるようにするためです)、ビュー自体である必要はありません。多くの場合、IoC マネージャーをコントローラーと考える方が適切です。これは、MVCVM の疑似パターンにつながります。この「コントローラー」の唯一の目的は、View と ViewModel のインスタンスの組み合わせを必要な人に提供することです。

ViewModel と View のクラスは、ペアになっている相手を気にする必要がなくなり、自分のレベルの他のクラスとのみ対話できるため、イベント アグリゲーター パターンはこれに非常に役立ちます。特定の View Model は、イベント "Update Load Progress" の送信者を気にする必要はなく、進行状況プロパティを設定してイベントの結果を処理する必要があります。

于 2013-03-28T18:46:40.577 に答える