👋

【WPF】PrismテンプレートXAMLにDataCotextを付与したときのエラーを解消する

2021/06/27に公開

はじめに

タイトルが長い・・・

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個ほどプロパティが設定される。

MainWindow.xaml
<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:mcmc:Ignorableの2行をあとで使う。

DataContextの自動生成について

後述のPrismプロジェクトの場合、Windowタグにマウスオーバーすると、DataContextの行を自動生成してくれる
一方、スタンダードなWPFの場合は、Windowタグにマウスオーバーしてもヒントしか表示されない(コードは自動生成されない)

本題の前段:Prismテンプレートを使ったアプリケーション

次に本題のPrismライブラリをつかったWPFアプリを作成する。

プロジェクトをつくる

Prism Blank Appを選択
.NET Core 3.1のプロジェクトが生成される。

DIライブラリはUnityを選択

ソリューションの中身

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

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:ClassHeight,Widthなどは省略)

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"
- 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に関する行が追加されている
逆に、dmsに関する行が削除されている

dmcの意味は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行のコードが追加される。

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/"
+           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"
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/"
         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>

(おまけ)デザインプレビューも便利にする

下記のようにIsDesignTimeCreatableTrueをセットすることで、XAMLプレビュー時にViewModelのインスタンスのプロパティ値が表示される

MainWindow.xaml
- d:DataContext="{d:DesignInstance Type=viewmodels:MainWindowViewModel}"
+ d:DataContext="{d:DesignInstance Type=viewmodels:MainWindowViewModel, IsDesignTimeCreatable=True}”

Windowのタイトルに、ViewModelのTitleプロパティの値"Prism Application"が表示される

ViewModelのコンストラクターに引数がある場合

下記のように、ViewModelのコンストラクターに引数をもたせる

MainWindowViewModel.cs
// ViewModelのコンストラクター
public MainWindowViewModel(IRegionManager regionManager)
{
}

すると、デザイン用のインスタンスが生成できず、XAMLプレビューにプロパティ値(Windowのタイトルなど)が表示されない

対処方法の1つとして、ViewModelにコンストラクターを2つ持たせることで回避できる

MainWindowViewModel.cs
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を割り当てられる

という、ないものねだり。

MainWondw.xaml
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

参考にさせていただいたサイト群

脚注
  1. XAML 名前空間と名前空間マッピング ↩︎ ↩︎

Discussion