sh1’s diary

プログラミング、読んだ本、資格試験、ゲームとか私を記録するところ

WPF Prism サンプルコードの学習3 (ViewModelLocator)

f:id:shikaku_sh:20211013180838p:plain:w400

WPF + .NET Core (5以降は Core は省略される) で Prism を使ってみよう。

使用している Prism のバージョンは次のとおり:

Prism Full App (.NET Core) テンプレートを体験する」の記事も参考になると思います。

関連記事は以下:

8. ViewModelLocator

View と ViewModel を紐づけて MVVM を成立させる基本となる機能。

<Window x:Class="ViewModelLocator.Views.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:prism="http://prismlibrary.com/"
        prism:ViewModelLocator.AutoWireViewModel="True"
        Title="{Binding Title}" Height="350" Width="525">
    <Grid>
        <ContentControl prism:RegionManager.RegionName="ContentRegion" />
    </Grid>
</Window>
public class MainWindowViewModel : BindableBase
{
}

View では prism:ViewModelLocator.AutoWireViewModel が、VM では BaindableBase が、それぞれ具体的に役割を担っている。

デフォルトでは、Prism ライブラリー ViewModelLocationProvider が対応する ViewModel を取得している。

  • フォルダー名(+配置)の規則
    View は「Views.ファイル名」、ViewModel は「ViewModels.ファイル名」
  • ファイル名(+配置)の規則
    View は「名称の末尾に View」、ViewModel は「名称の末尾に ViewModel」
  • 例 (Sample という名称をつける場合) Views.SampleView.xaml
    ViewModel.SampleViewModel.cs

基本的には、意識せずに機能することが目的のものなので、Prism を利用すると View と ViewModel は自動で紐づくようになる、くらいの理解がよいと思います。

9. ChangeConvention

Change Convention の名前のとおり、約束(しきたり)を変更する機能です。

デフォルトでは、Prism ライブラリー ViewModelLocationProvider が View に対応する ViewModel を取得している、「フォルダー名(+配置)の規則」「ファイル名(+配置)の規則」を変更するということです。

public partial class App : PrismApplication
{
    protected override void ConfigureViewModelLocator()
    {
        base.ConfigureViewModelLocator();

        ViewModelLocationProvider.SetDefaultViewTypeToViewModelTypeResolver((viewType) =>
        {
            var viewName = viewType.FullName;
            var viewAssemblyName = viewType.GetTypeInfo().Assembly.FullName;
            var viewModelName = $"{viewName}ViewModel, {viewAssemblyName}";
            return Type.GetType(viewModelName);
        });
    }
}

わかりにくいけど、以下の約束(しきたり)で View と ViewModel をつなぐように変更されています:

  • フォルダー名(+配置)の規則
    View は「Views.ファイル名」、ViewModel は「ViewModels.ファイル名」
  • ファイル名(+配置)の規則
    View は「名称の末尾に View」、ViewModel は「名称の末尾に ViewModel」
  • 例 (Sample という名称をつける場合) Views.SampleView.xaml
    Views.SampleViewModel.cs

ViewModelLocator の機能は、On Rails の規約に則ってコーディングすることで、開発者の負担を減じることのようなものです。でも、柔軟性に欠けるので独自の On Rails に微修正するためのもの、というカスタマイズの位置です。

10. CustomRegistrations

Custom Registrations は、Change Convention とは異なり、約束(しきたり)を変更するのではなくて、View に対応する ViewModel の型を指定しておくことで、Custom Registrations に View と ViewModel の紐づけを優先的に解決する手法です。

public partial class App : PrismApplication
{
    protected override void ConfigureViewModelLocator()
    {
        base.ConfigureViewModelLocator();

        // type / type
        //ViewModelLocationProvider.Register(typeof(MainWindow).ToString(), typeof(CustomViewModel));

        // type / factory
        //ViewModelLocationProvider.Register(typeof(MainWindow).ToString(), () => Container.Resolve<CustomViewModel>());

        // generic factory
        //ViewModelLocationProvider.Register<MainWindow>(() => Container.Resolve<CustomViewModel>());

        // generic type
        ViewModelLocationProvider.Register<MainWindow, CustomViewModel>();
    }
}

Custom Registrations のやり方は、View と ViewModel を具体的にひとつひとつ結びつける手法で、Change Convention はまとめて結びつける約束(しきたり)を定めること、なので用途が異なります。

注意する点として、View に複数の ViewModel を割り当てすることができてしまう。実行時もエラーにならず、どちらかの ViewModel とのみ紐づけられる。

ViewModelLocationProvider.Register<MainWindow>(() => Container.Resolve<CustomViewModel>());
ViewModelLocationProvider.Register<MainWindow>(() => Container.Resolve<CustomViewModel2>());

意図しない挙動をする恐れがあるため、Custom Registrations は開発者が管理できる範囲で少数利用することが望ましいと思われる。

View と ViewModel の紐づけは約束(しきたり)を守るものであったり、On Rails(レールに従う)であることが望ましくて、紐づけの規則がないと混乱するだけなので注意したい。

まとめ

ViewModelLocator についてまとめた。

ViewModelLocator は View と ViewModel を自動的に View の DataContext に設定してくれる。原則的には、約束(しきたり)に則ったフォルダー+ファイル名の名付け方をすること。

どうしても、デフォルトの約束(しきたり)では、具合がわるいなら ChangeConvention か CustomRegistrations を検討する。

  • すべての規則を変更したい = ChangeConvention
  • 一部分だけ変更したい = CustomRegistrations

参考