【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