「Androidは電気羊の夢を見るか」を読みたい管理者のブログ

仕事などでの色々な発見を記事にしてます。不定期更新。

ContentControl経由でViewを呼び出すとViewModelが変わっちゃうらしい

かつてこんな記事を書きました。
bignight.hatenablog.com
それには例外がありました。
ContentControl経由でViewを呼び出すとViewModelを共有してくれない😱
さあどうする?というわけで実験してみました。
(ちなみにContentControl経由で子コントロールを呼ぶ方法は以下の記事を参考にしてください。今回はDataTemplate経由で子コントロールを呼びます。)
bignight.hatenablog.com

簡単なコードです
Viewはこんな感じ

<Window x:Class="WpfApp1.Window1"
        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="Window1" Height="450" Width="800">
    <Window.Resources>
        <DataTemplate x:Key="originalTemplate">
            <Border BorderBrush="Orange"  BorderThickness="1">
                <local:myControl/>
            </Border>
        </DataTemplate>
        <local:myControlViewModel x:Key="myControlViewModel"/>
    </Window.Resources>
    <StackPanel>
        <TextBlock Text="Content:windowViewModel"/>
        <!--ContentControl経由でViewを呼ぶ。ContentはwindowViewModelとする。-->
        <ContentControl Content="{StaticResource myControlViewModel}" ContentTemplate="{StaticResource originalTemplate}"/>
        <TextBlock Text="Content:None"/>
        <!--ContentControl経由でViewを呼ぶ。Contentは指定しない。-->
        <ContentControl ContentTemplate="{StaticResource originalTemplate}"/>
        <TextBlock Text="Content:Binding"/>
        <!--ContentControl経由でViewを呼ぶ。Contentは{Binding}とする。-->
        <ContentControl Content="{Binding}" ContentTemplate="{StaticResource originalTemplate}"/>

        <TextBlock Text="{Binding Text}"/>

    </StackPanel>
</Window>

コードビハインドでViewModelを指定します。

    public partial class Window1 : Window
    {
        public Window1()
        {
            InitializeComponent();
            DataContext = new Window1ViewModel();
        }
    }

メインのWindowのViewModelの中身

    public class Window1ViewModel
    {
        public string Text { get; set; } = "これはWindow1ViewModel";
    }

子コントロールはこんな感じ
TextBlockのTextにTextをバインドさせてみます。

<UserControl x:Class="WpfApp1.myControl"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:WpfApp1"
             mc:Ignorable="d" 
             d:DesignHeight="450" d:DesignWidth="800"
             Name="mine">
    <StackPanel>
        <StackPanel Orientation="Horizontal">
            <TextBlock Text="Binding:"/>
            <TextBlock Text="{Binding}"/>
        </StackPanel>
        <StackPanel Orientation="Horizontal">
            <TextBlock Text="Binding Text:"/>
            <TextBlock Text="{Binding Text}"/>
        </StackPanel>
    </StackPanel>
</UserControl>

子コントロールのViewModel(最初にStaticResourceで指定したやつ)

    public class myControlViewModel
    {
        public string Text { get; set; } = "これはmyControlViewModel";
    }

私の当初の予想では「"これはWindow1ViewModel";」が3回表示されると思ったんですが、結果は違いました。
実行結果はこんな感じ
オレンジの枠で囲んであるところが子コントロールです。

Content="{Binding}" と記述したときだけViewModelの共有が行われています。

どうやらContentControlのViewModelはContentに何を指定したかに依存するようですね。
ContentControlを継承したButtonなんかどうなるんだろう。
まだまだ知らないことが沢山あります。