子の ViewModel のプロパティの更新通知を受け取って、親の View を更新する方法

More than 3 years have passed since last update.

ViewModel が子として別の ViewModel を持っているパターンというのは結構よくあることだと思いますが、

その際、子の ViewModel のプロパティの更新通知を受け取って親の View を更新したいということもあると思います。

(例えば、リストビューの要素数をステータスバーに表示したい、など。)

これを、できれば XAML の指定のみでやりたいと思って調べて、実際にやってみました。


XAML の記述

まずは、XAML の記述からです。


MainWindow.xaml

<Window x:Class="WPF_MVVM.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:WPF_MVVM"
xmlns:local_vm="clr-namespace:WPF_MVVM.ViewModels"
xmlns:local_v="clr-namespace:WPF_MVVM.Views"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<local_vm:MainViewModel />
</Window.DataContext>
<Grid>
<Label x:Name="label" Content="{Binding TextBoxViewModel.Text}" HorizontalAlignment="Left" Margin="39,31,0,0" VerticalAlignment="Top"/>
<local_v:TextBoxView x:Name="textBox" HorizontalAlignment="Left" Height="23" Margin="39,82,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="120" DataContext="{Binding TextBoxViewModel}"/>
<Button x:Name="button" Content="Button" HorizontalAlignment="Left" Margin="53,149,0,0" VerticalAlignment="Top" Width="75" Command="{Binding ButtonCommand}" />
</Grid>
</Window>


TextBoxView.xaml

<TextBox x:Class="WPF_MVVM.Views.TextBoxView"

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:WPF_MVVM.Views"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300"
Text="{Binding Text}">
</TextBox>

MainWindow.xaml がウィンドウの XAML、

TextBoxView.xaml が配置しているテキストボックスの XAML です。

ウィンドウには


  • ラベル

  • テキストボックス

  • ボタン

を配置しています。

ラベル、テキストボックスそれぞれに TextBoxViewModel::Text をバインドしています。

ボタンを押すたびに TextBoxViewModel::Text「True」「False」というテキストが交互に設定され、

それがラベル、テキストボックスの双方に表示される、という仕様になっています。

注目するのはここです。

<local_v:TextBoxView ... DataContext="{Binding TextBoxViewModel}"/>

これは、テキストボックスの DataContext に Window.DataContext.TextBoxViewModel をバインドしています。

こうすることで、子として持っている ViewModel と親の View で配置されているコントロールの DataContext を XAML のみでバインドできます。


C# コードの記述

次に、C# コードの記述です。実装には Livet を使用しています。


MainViewModel.cs

using Livet;

using Livet.Commands;

namespace WPF_MVVM.ViewModels
{
// ウィンドウの ViewModel
class MainViewModel : ViewModel
{
// ボタンの Command とバインドしているプロパティ
public ViewModelCommand ButtonCommand
{
get
{
return _ButtonCommand ?? (_ButtonCommand = new ViewModelCommand(ClickButton));
}
}
private ViewModelCommand _ButtonCommand;

// テキストボックスの ViewModel
public TextBoxViewModel TextBoxViewModel
{
get
{
return _TextBoxViewModel;
}
set
{
_TextBoxViewModel = value;
}
}
public TextBoxViewModel _TextBoxViewModel;

// コンストラクタ
public MainViewModel()
{
_TextBoxViewModel = new TextBoxViewModel();
}

// テキスト変更用のフラグ
private bool _Flag = false;

// ボタンを押した時に実行される処理
private void ClickButton()
{
_Flag = !_Flag;
_TextBoxViewModel.Text = _Flag.ToString();
}
}
}



TextBoxViewModel.cs

using Livet;

namespace WPF_MVVM.ViewModels
{
// テキストボックスの ViewModel
class TextBoxViewModel : ViewModel
{
// テキストボックスに表示するテキスト
public string Text
{
get
{
return _Text;
}
set
{
_Text = value;
RaisePropertyChanged(nameof(Text));
}
}
private string _Text;
}
}


MainWindowView.cs がウィンドウの ViewModel、

TextBoxViewModel.cs が配置しているテキストボックスの ViewModel です。

データバインドに使用しているプロパティ TextBoxViewModel::Text は自分の更新通知を出しているだけです。