【WPF】PrismテンプレートXAMLにDataCotextを付与したときのエラーを解消する
はじめに
タイトルが長い・・・
WPFでXAMLを書くときは、インテリセンスを有効にして楽にコーディングを行いたい。
インテリセンスをきかせるには、XAML(View)のDataContextプロパティに、なんらかのインスタンスを割り当てる必要がある。
(MVVMではHogeViewModelクラスを割り当てる)
Visual Studio拡張機能であるPrism Template Packを用いてテンプレートプロジェクトをつくり、DataContextを自動生成すると、エラーが起きる。
(自動生成とは、Visual Studioの電球マーク💡が出たときにCtrl + .で実行されるコード生成を指す)
この記事では、そのエラー回避について検証する。
前段:スタンダードなWPFアプリケーション
まず本題の前に、Prismライブラリを使用しないスタンダードなWPFアプリを確認する。
Visual Studioで新しいプロジェクトを作成する

MainWindow.xamlが生成される。
Windowタグには、10個ほどプロパティが設定される。
<Window x:Class="WpfApp1.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:local="clr-namespace:WpfApp1"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
</Grid>
</Window>
この中のxmlns:mcとmc:Ignorableの2行をあとで使う。
DataContextの自動生成について
後述のPrismプロジェクトの場合、Windowタグにマウスオーバーすると、DataContextの行を自動生成してくれる。
一方、スタンダードなWPFの場合は、Windowタグにマウスオーバーしてもヒントしか表示されない(コードは自動生成されない)

本題の前段:Prismテンプレートを使ったアプリケーション
次に本題のPrismライブラリをつかったWPFアプリを作成する。
プロジェクトをつくる
Prism Blank Appを選択
.NET Core 3.1のプロジェクトが生成される。

DIライブラリはUnityを選択

ソリューションの中身

Prismテンプレートによって生成されたMainWindow.xaml

<Window x:Class="BlankCoreApp1.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>
MainWindow.xamlを比較する
スタンダードなMainWindow.xamlと、Prismのそれとを比較する
(x:ClassやHeight,Widthなどは省略)
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"
- xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
- xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
- xmlns:local="clr-namespace:WpfApp1"
- mc:Ignorable="d"
prismに関する行が追加されている
逆に、dやmsに関する行が削除されている
dやmcの意味はMicrosoft公式のドキュメントに詳しく書かれている
"d:" は、デザイナー サポート (特に、Microsoft Visual Studio の XAML デザイン サーフェイスにおけるデザイナー サポート) を対象とした XAML 名前空間です。[1]
" mc:" は、XAML を読み取るためのマークアップ互換モードを示し、このモードをサポートします。 通常、"d:" プレフィックスは属性 mc:Ignorable に関連付けられます。 この手法により、ランタイムの XAML パーサーで "d:" のデザイナー属性を無視することができます。[1:1]
DataContextプロパティを追加する
Windowタグにマウスオーバーすると、クイック操作のヒントが表示される
前段のスタンダードなWPFではこのヒントが表示されなかった。
DataContextを設定すると、データバインディングで作業するときにInteliSenseが強化されます

さらに詳細をみると、DataContextをViewModelに設定の選択肢が現れる
デザインDataContextをMainWindowViewModel {BlankCoreApp1.ViewModel}に設定

DataContextをViewModelに設定を選択すると、新しく3行のコードが追加される。
<Window x:Class="BlankCoreApp1.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/"
+ xmlns:viewmodels="clr-namespace:BlankCoreApp1.ViewModels"
+ xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+ d:DataContext="{d:DesignInstance Type=viewmodels:MainWindowViewModel}"
prism:ViewModelLocator.AutoWireViewModel="True"
Title="{Binding Title}" Height="350" Width="525" >
<Grid>
<ContentControl prism:RegionManager.RegionName="ContentRegion" />
</Grid>
</Window>
DataContextを設定したことで、ViewModelのTitleプロパティに対してインテリセンスが働くようになる🎉

本題:DataContextプロパティを追加したことによるエラー
DataContextを自動追加した直後の状態でデバッグの開始(F5)をすると、エラーが起きる

回避策
エラーを回避するためには、Windowに2つのプロパティを追加する
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"mc:Ignorable="d"
<Window x:Class="BlankCoreApp1.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/"
xmlns:viewmodels="clr-namespace:BlankCoreApp1.ViewModels"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
d:DataContext="{d:DesignInstance Type=viewmodels:MainWindowViewModel}"
+ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+ mc:Ignorable="d"
prism:ViewModelLocator.AutoWireViewModel="True"
Title="{Binding Title}" Height="350" Width="525" >
<Grid>
<ContentControl prism:RegionManager.RegionName="ContentRegion" />
</Grid>
</Window>
(おまけ)デザインプレビューも便利にする
下記のようにIsDesignTimeCreatableにTrueをセットすることで、XAMLプレビュー時にViewModelのインスタンスのプロパティ値が表示される
- d:DataContext="{d:DesignInstance Type=viewmodels:MainWindowViewModel}"
+ d:DataContext="{d:DesignInstance Type=viewmodels:MainWindowViewModel, IsDesignTimeCreatable=True}”
Windowのタイトルに、ViewModelのTitleプロパティの値"Prism Application"が表示される

ViewModelのコンストラクターに引数がある場合
下記のように、ViewModelのコンストラクターに引数をもたせる
// ViewModelのコンストラクター
public MainWindowViewModel(IRegionManager regionManager)
{
}
すると、デザイン用のインスタンスが生成できず、XAMLプレビューにプロパティ値(Windowのタイトルなど)が表示されない

対処方法の1つとして、ViewModelにコンストラクターを2つ持たせることで回避できる
public string Title { get; } = "A";
// 表示用コンストラクター
public MainWindowViewModel()
{
Debug.WriteLine("A");
}
// 実際に使うコンストラクターはこちら
public MainWindowViewModel(IRegionManager regionManager)
{
Title = "B";
Debug.WriteLine("B");
}
デザイン画面ではタイトルがAと表示される

実行中はタイトルがBと表示される

コンソールには"B"とだけ表示されることから、実行時は引数ありのコンストラクターしか使っていないことがわかる。
おわりに
つまるところ、PrismテンプレートでつくったプロジェクトのXAMLファイルに
最初から下記3行があれば
あとはVisual Studioのヒント機能によって自動でDataContextを割り当てられる
という、ないものねだり。
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
付録
バージョン情報
動作環境は、Visual Studio2019 CommunityをインストールしたWindows 10
Visual StudioにはPrism Template Pack拡張機能をインストールしている
Visual Studio: 16.10.2

Prism Templete Pack: 2.3.0

Prism.Unity: 8.1.97

参考にさせていただいたサイト群
- XAML 名前空間と名前空間マッピング
- Xamlにdesign instanceを設定(C# WPF)
- Visual Studioでのデザイン時DataContextに適切なViewModelを供給するMarkupExtension
- XAML内でDataContextのプロパティにIntelliSenseが効くようにする
Discussion