確かに正しい方向に進んでいると思いますが、ここにはいくつかの問題があります。
まず、ビュー モデルでビューをインスタンス化するべきではありません。ビュー モデルがビューを認識するようになるとすぐに、パターンをほぼ破ったことになります。
var viewTwo = new ViewTwo(new ViewModelTwo());
ビューの作成は、マスター ビューが担当する必要があります。実際、ビューの作成について心配する必要さえありません。ビューには を使用できるDataTemplate
からです。それについては後で説明します。
まず、View ModelsをViewsから分離する必要があります。ここに私が提案するものがあります:
物事を汎用的に保つために、ある種のベースclass
またはinterface
ビューモデルが必要になります。その理由はすぐにわかります。簡単な例から始めましょう。
public abstract class ViewModel : INotifyPropertyChanged
{
public event EventHandler OnClosed;
public event EventHandler OnOpened;
//Don't forget to implement INotifyPropertyChanged.
public bool IsDisplayed { get; private set; }
public void Open()
{
IsDisplayed = true;
//TODO: Raise the OnOpened event (Might be a better idea to put it in the IsDisplayed getter.
}
public void Close()
{
IsDisplayed = false;
//TODO: Raise the OnClosed event.
}
}
もちろん、これは非常に単純な基本ビュー モデルです。後でこれを拡張できます。これの主な理由は、現在のページの表示を担当するマスタービュー モデルを作成できるようにするためです。マスター ビュー モデルの簡単な例を次に示します。
public class MasterViewModel : INotifyPropertyChanged
{
//Don't forget to implement INotifyPropertyChanged.
public ViewModel CurrentPage { get; private set; }
public MasterViewModel()
{
//This is just an example of how to set the current page.
//You might want to use a command instead.
CurrentPage = new MovieViewModel();
}
//TODO: Some other master view model functionality, like exiting the application.
}
INotifyPropertyChanged
同じコードを何度も再実装する必要があるのではなく、ある種の基本クラスの方がおそらく優れていることに注意してください。
これMasterViewModel
は非常に単純で、現在のページを保持するだけですが、マスターを持つ目的は、アプリを閉じるなど、アプリケーションレベルのコードを実行できるようにすることです。これにより、このロジックを他のビューモデルから遠ざけることができます.
さて、良いものに移ります。
あなたの詳細はその親と関係があるため、それを管理するのは親の責任であると言うのは理にかなっています. この場合、マスター/ディテール ビュー モデルは次のようになります。
public class MovieViewModel : ViewModel
{
protected PickGenreViewModel ChildViewModel { get; private set; }
public MovieViewModel()
{
ChildViewModel = new PickGenreViewModel();
//TODO: Perhaps subscribe to the closed event?
}
//Just an example but an important thing to note is that
//this method is protected because it's the MovieViewModel's
//responsibility to manage it's child view model.
protected void PickAGenre()
{
ChildViewModel.Open();
}
//TODO: Other view model functionality.
}
これで、ある種のビュー モデル構造ができました。「ビューについてはどうですか?」と疑問に思われることでしょう。ここで のDataTemplate
出番です。
WPF では、ビューをに割り当てるType
ことができます。たとえば、次のように XAML でMovieView
を に割り当てることができます。MovieViewModel
xmlns:Views="clr-namespace:YourNamespace.Views"
xmlns:ViewModels="clr-namespace:YourNamespace.ViewModels"
...
<DataTemplate DataType="{x:Type ViewModels:MovieViewModel}">
<Views:MovieView/>
</DataTemplate>
マスター ビューを取得して現在のページのビューを実際に表示するには、 を作成し、それをContentPresenter
にバインドするだけです。マスター ビューは次のようになります。Content
CurrentPage
<Window
...
xmlns:ViewModels="clr-namespace:YourNamespace.ViewModels">
<Window.DataContext>
<ViewModels:MasterViewModel/>
</Window.DataContext>
<Grid>
<ContentPresenter Content="{Binding CurrentPage}"/>
</Grid>
これをさらに拡張するには、 for it's childMasterView
を含める必要があるのは だけでなく、 it's childにも必要です。同じ方法をもう一度使用できます。ContentPresenter
MovieView
PickGenreViewModel
<Grid>
<!-- The main view code for the movie view -->
...
<Border Visibility="{Binding ChildViewModel.IsDisplayed, Converter=...">
<ContentPresenter Content="{Binding ChildViewModel}"/>
</Border>
</Grid>
注: ブール値から可視性へのコンバーターを使用して、子コンテンツを表示するかどうかを決定します。
このメソッドを使用すると、ビューのインスタンス化DataTemplate
について心配する必要はありません。とContentPresenter
がそれを処理するため、心配する必要があるのは、ビュー モデルを適切なビューにマッピングすることだけです。
ふぅ!それは多くのことを吸収するものでした。
これから取り上げる主なポイントは次のとおりです。
- ビュー モデルでビューを作成するべきではありません。UI は UI であり、Data は Data であることを忘れないでください。
- ビュー モデルの責任は、ビュー モデルの所有者にあり、親子関係では、ビュー モデルの管理者ではなく、親に子を管理させるのが理にかなっています。
最後の注意点として、これを実現する方法は他にも 1 つ以上あります。先ほど述べたように、ビューとビュー モデルの作成/削除を担当するある種のビューおよびビュー モデル マネージャーです。