KatsuYuzuのブログ

.NET の開発をメインとした日記です。

d:DataContextでデザイナ用にViewModelを設定する

実行時に引数を伴って生成されるViewModelをデザイナで使用したいことってありませんか?
そういった時はデザイン時属性のd:DataContextを使いましょう。

さくっとやってみる

下記のシナリオでやってみます。

  1. まず、普通(?)にXAMLでDataContextにViewModelを指定する。
  2. ViewModelのConstructorで引数を受け取るようにする。
  3. d:DataContextにサンプルデータを設定する。

まず、普通(?)にXAMLでDataContextにViewModelを指定する。

適当にViewModelを作ってXAMLで指定します。

    Public Class HelloWorldViewModel
        Public Property Display() As String = "Hello, 実行時!"
    End Class
<UserControl x:Class="SilverlightApplication1.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:SilverlightApplication1.ViewModels"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    d:DesignHeight="300" d:DesignWidth="400">
    <UserControl.DataContext>
        <local:HelloWorldViewModel/>
    </UserControl.DataContext>
    <Grid x:Name="LayoutRoot" Background="White">
        <TextBlock Text="{Binding Display}"/>
    </Grid>
</UserControl>

この状態だと、デザイナでも実行時でも「Hello, 実行時!」と表示されます。

ViewModelのConstructorで引数を受け取るようにする。

引数を伴うConstructorを追加します。

        Public Sub New(ByVal display As String)
            Me.Display = display
        End Sub

すると、例外が発生します。

d:DataContextにサンプルデータを設定する。

これを解決するためにはデザイン用のサンプルデータを作成してd:DataContextに設定します。
まず、XAMLファイルを追加してビルドアクションを「DesignData」にします。次にサンプルデータを編集します。

<local:HelloWorldViewModel
    xmlns:local="clr-namespace:SilverlightApplication1.ViewModels"
    Display="Hello, Design!"
/>

これをd:DataContextで設定して、例外の原因になっている部分はコメントアウトします。

<UserControl x:Class="SilverlightApplication1.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:SilverlightApplication1.ViewModels"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    d:DataContext="{d:DesignData /SampleData/SampleHelloWorld.xaml}"
    d:DesignHeight="300" d:DesignWidth="400">
    <!--<UserControl.DataContext>
        <local:HelloWorldViewModel/>
    </UserControl.DataContext>-->
    <Grid x:Name="LayoutRoot" Background="White">
        <TextBlock Text="{Binding Display}"/>
    </Grid>
</UserControl>

これでデザイナを開いてみると……

バッチリ!Constructorのシグネチャに関わらず表示されています。
(ちなみに、デザイン時属性はコンパイル時に無視されるので、このまま実行しても何も表示されません。)

あとは、処理の中で実行時に設定されるように実装します。

Partial Public Class MainPage
    Inherits UserControl
    Public Sub New()
        InitializeComponent()
        Me.DataContext = New ViewModels.HelloWorldViewModel("Hello, Constructor with Parameter!")
    End Sub
End Class

まとめ

デザイン時属性を使ってデザインをしよう!デザイン時属性はコンパイル時に無視されるので安心!

今回のことは「引数を伴うConstructorだけを持つViewModelをデザイナで使うにはどうしたらいいんだろう」とつぶやいていたところ、@Posaune さんが教えてくださいました。

ありがとうございます。