本記事は転載です。
原文著者: RyzenAdorer
原文タイトル: .NET Core 3 WPF MVVM フレームワーク Prism シリーズ データバインディング
1. Prism のインストール
- パッケージ管理コンソールを使用
Install-Package Prism.Unity -Version 7.2.0.1367
' -Version 7.2.0.1367' を削除して最新バージョンを取得することもできます。
- ソリューションの NuGet パッケージ管理を使用

上記で疑問があるかもしれません。なぜ prism をインストールすると Prism.Unity が関係するのか? Unity は IOC コンテナーであり、Prism 自体が IOC をサポートしており、現在公式にはいくつかの IOC コンテナーをサポートしています。

-
- unity は Microsoft 公式であり、prism のコンポーネント化をサポートしているため、prism.unity の使用を推奨します。公式ドキュメントでは、prism7 は prism.Mef をサポートせず、Prism 7.1 は prism.Autofac をサポートしません。
-
- prism.unity をインストールすると、すべての prism コアライブラリが含まれています。アーキテクチャは以下のとおりです。

2. データバインディングの実装
まず Views フォルダーと ViewModels フォルダーを作成し、MainWindow を Views フォルダーに配置し、ViewModels フォルダーの下に MainWindowViewModel クラスを作成します。以下のようになります。

xaml コードは以下のとおりです。
<Window
x:Class="PrismSample.Views.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:prism="http://prismlibrary.com/"
xmlns:local="clr-namespace:PrismSample"
mc:Ignorable="d"
Title="MainWindow"
Height="450"
Width="800"
prism:ViewModelLocator.AutoWireViewModel="True"
>
<StackPanel>
<TextBox
Text="{Binding Text}"
Margin="10"
Height="100"
FontSize="50"
Foreground="Black"
BorderBrush="Black"
/>
<button
Height="100"
Width="300"
Content="Click Me"
FontSize="50"
Command="{Binding ClickCommnd}"
/>
</StackPanel>
</Window>
ViewModel コードは以下のとおりです。
using Prism.Commands;
using Prism.Mvvm;
namespace PrismSample.ViewModels
{
public class MainWindowViewModel:BindableBase
{
private string _text;
public string Text
{
get { return _text; }
set { SetProperty(ref _text, value); }
}
private DelegateCommand _clickCommnd;
public DelegateCommand ClickCommnd =>
_clickCommnd ?? (_clickCommnd = new DelegateCommand(ExecuteClickCommnd));
void ExecuteClickCommnd()
{
this.Text = "Click Me!";
}
public MainWindowViewModel()
{
this.Text = "Hello Prism!";
}
}
}
プログラムを起動します。

Click Me ボタンをクリックします。

prism を使用してデータバインディングを正常に実装でき、View と ViewModel が完全にフロントエンドとバックエンドで分離されていることがわかります。
しかし、ここで別の問題が生じます。prism の規則に従って View と ViewModel を Views と ViewModels に強制的に配置したくない場合や、プロジェクトの命名規則がそれぞれ異なる場合はどうすればよいでしょうか。その場合は、以下の方法を使用します。
- 命名規則の変更
会社の命名規則が非常に厳しく、プロジェクト構造が次のようになる場合(こんな会社は辞めたほうがいいでしょう)。

まず、App で prism を導入し、'Application' を 'prism:PrismApplication' に変更し、StartupUri を削除する必要があります。
xaml コードは以下のとおりです。
<prism:PrismApplication
x:Class="PrismSample.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:prism="http://prismlibrary.com/"
xmlns:local="clr-namespace:PrismSample"
>
<Application.Resources> </Application.Resources>
</prism:PrismApplication>
cs のバックエンドコードは以下のとおりです。
using Prism.Unity;
using Prism.Ioc;
using Prism.Mvvm;
using System.Windows;
using PrismSample.Viewsb;
using System;
using System.Reflection;
namespace PrismSample
{
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : PrismApplication
{
// スタートページの設定
protected override Window CreateShell()
{
return Container.Resolve<MainWindow>();
}
protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
}
// 規則の構成
protected override void ConfigureViewModelLocator()
{
base.ConfigureViewModelLocator();
ViewModelLocationProvider.SetDefaultViewTypeToViewModelTypeResolver((viewType) =>
{
var viewName = viewType.FullName.Replace(".Viewsb.", ".ViewModelsa.OhMyGod.");
var viewAssemblyName = viewType.GetTypeInfo().Assembly.FullName;
var viewModelName = $"{viewName}Test, {viewAssemblyName}";
return Type.GetType(viewModelName);
});
}
}
}
上記の2行が重要です。
".Viewsb." は View が存在するフォルダーの名前空間、".ViewModelsa.OhMyGod." は ViewModel が存在する名前空間を表します。
var viewName = viewType.FullName.Replace(".Viewsb.", ".ViewModelsa.OhMyGod.");
Test は ViewModel のサフィックスを表します。
var viewModelName = $"{viewName}Test, {viewAssemblyName}";
- カスタム ViewModel の登録
新しい Foo クラスをカスタムクラスとして作成します。コードは以下のとおりです。
using Prism.Commands;
using Prism.Mvvm;
namespace PrismSample
{
public class Foo:BindableBase
{
private string _text;
public string Text
{
get { return _text; }
set { SetProperty(ref _text, value); }
}
public Foo()
{
this.Text = "Foo";
}
private DelegateCommand _clickCommnd;
public DelegateCommand ClickCommnd =>
_clickCommnd ?? (_clickCommnd = new DelegateCommand(ExecuteClickCommnd));
void ExecuteClickCommnd()
{
this.Text = "Oh My God!";
}
}
}
App.cs のコードを変更します。
protected override void ConfigureViewModelLocator()
{
base.ConfigureViewModelLocator();
ViewModelLocationProvider.Register<MainWindow, Foo>();
//ViewModelLocationProvider.SetDefaultViewTypeToViewModelTypeResolver((viewType) =>
//{
// var viewName = viewType.FullName.Replace(".Viewsb.", ".ViewModelsa.OhMyGod.");
// var viewAssemblyName = viewType.GetTypeInfo().Assembly.FullName;
// var viewModelName = $"{viewName}Test, {viewAssemblyName}";
// return Type.GetType(viewModelName);
//});
}
実行します。

ボタンをクリックします。

命名規則を変更するコードをコメントアウトしなくても、実行結果は同じです。したがって、次の結論を導き出せます。
このようなリフレクションを使用しない直接のカスタム登録方法は優先度が高くなります。公式ドキュメントでもこの方法の効率が高いと説明されています。
また、公式では4つの方法が提供されており、残りの3つの登録方法は以下のとおりです。
ViewModelLocationProvider.Register(typeof(MainWindow).ToString(), typeof(MainWindowTest));
ViewModelLocationProvider.Register(typeof(MainWindow).ToString(), () => Container.Resolve<Foo>());
ViewModelLocationProvider.Register<MainWindow>(() => Container.Resolve<Foo>());