📖

WPFで子ViewにBindingを行うと子のDataContextを参照する

2022/08/04に公開

親Viewが子のViewを含んでいる。
それぞれがそれぞれのViewModelを生成している。
という状況です。

コードにて状況説明すると

// ChildView.xaml

<UserControl ...省略...>
    <UserControl.DataContext>
        <local:ChildViewModel/>
    </UserControl.DataContext>
    <Grid>
        <Button Content="Child"
                Visibility="{Binding Visibility, Mode=OneWay}"/>
    </Grid>
</UserControl>
// ChildViewModel.cs

using System.Windows;

namespace ChildViewModelTest
{
    internal class ChildViewModel : ViewModelBase
    {
        public Visibility Visibility => Visibility.Visible;
    }
}
// MainWindow.xaml

<Window ...省略...>
    <Window.DataContext>
        <local:MainWindowViewModel/>
    </Window.DataContext>
    <Grid>
        <local:ChildView Visibility="{Binding Visibility, Mode=OneWay}"/>
    </Grid>
</Window>
// MainWindowViewModel.cs

using System.Windows;

namespace ChildViewModelTest
{
    internal class MainWindowViewModel : ViewModelBase
    {
        public Visibility Visibility => Visibility.Visible;
    }
}

こんな感じ。
Modelが作られていないとか、その変数なに?とかはここでは些末な問題なのでご容赦ください。
ViewModelBaseはDataContextに設定するためのINotifyPropertyChangedを実装したものと考えてください。

さてここで、親Viewから子ViewのVisibilityを制御したいと思ったので、以下変更を加えました。

// MainWindowViewModel.cs

internal class MainWindowViewModel : ViewModelBase
{
    // public Visibility Visibility => Visibility.Visible;
    public Visibility Visibility => Visibility.Hidden;
}

これでWindowを開いた時にChildViewは見えなくなるだろう、と思いましたが、
実行してみると見た目上に変化なし。

原因はタイトルにある通りですが、
どうやら、子ViewへのBindingは子のDataContextのプロパティが適用されるようです。
この場合はChildViewModel.Visibilityを参照しているので、何も変わらなかったと。

VisualStudio使ってて、MainWindow.xaml上でBindingしているVisibilityの定義に移動する(F12)と自分のViewModelに飛ぶので、非常に混乱しました。たまたま同じ名前のプロパティがあったので、余計に紛らわしかったです。

仕様なのか、どうしてこうなるのか、よく分かっていませんが、
今回の目的としてはVisibilityの制御なので、親要素を作ってそちらにBindingすることで事なきを得ました。

<Window ...省略...>
    <Window.DataContext>
        <local:MainWindowViewModel/>
    </Window.DataContext>
    <Grid>
        <Grid Visibility="{Binding Visibility, Mode=OneWay}"> <!-- 親要素で制御 -->
            <local:ChildView />
        </Grid>
    </Grid>
</Window>

Discussion