プログラムを中心とした個人的なメモ用のブログです。 タイトルは迷走中。
内容の保証はできませんのであしからずご了承ください。

2016/09/11

WPF + MVVM の勉強1:データバインディング

update2017/11/17 event_note2016/09/10 15:28

C# も WindowsForms もちょっとしか触ったことがない人が、WPF + MVVM でアプリケーションを作成するために勉強したことをまとめてみる記事です。
間違っているところがあれば指摘していただけると嬉しいです。

作成するサンプルプログラム

ここではサンプルとして、TextBox と Label を用意し、TextBox の中身をそのまま Label に表示するだけの簡単なプログラムを作成してみます。

サンプルコード

まず先にコードを示して、解説は後で行います。

View

XAML とコードビハインドです。

<Window x:Class="WpfApplication1.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:WpfApplication1"
    mc:Ignorable="d"
    Title="MainWindow" Height="350" Width="525">
    <Grid>
        <TextBox x:Name="textBox" HorizontalAlignment="Left" Height="23" Margin="10,10,0,0" TextWrapping="Wrap" Text="{Binding SampleText, UpdateSourceTrigger=PropertyChanged}" VerticalAlignment="Top" Width="120"/>
        <Label x:Name="label" Content="{Binding SampleLabel}" HorizontalAlignment="Left" Margin="10,38,0,0" VerticalAlignment="Top"/>
    </Grid>
</Window>
/// <summary>
/// MainWindow.xaml の相互作用ロジック
/// </summary>
public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();

        // バインディング対象のインスタンスを、MainWindowViewModelに設定
        this.DataContext = new MainWindowViewModel();
    }
}

ViewModel

次に ViewModel のコードです。

/// <summary>
/// MainWindowに対するViewModel
/// </summary>
class MainWindowViewModel
{
    // バインディングされる値を保持するフィールド
    private string sampleText_;

    // バインディング対象のプロパティ
    public string SampleText
    {
        get
        {
            return sampleText_;
        }
        set
        {
            sampleText_ = value;
            SampleLabel = value;// ラベルの値も連動させる
        }
    }

    // バインディングされる値を保持するフィールド
    private string sampleLabel_ = "";

    // バインディング対象のプロパティ
    public string SampleLabel
    {
        get
        {
            return sampleLabel_;
        }
        set
        {
            sampleLabel_ = value;
        }
    }

    /// <summary>
    /// コンストラクタ
    /// </summary>
    public MainWindowViewModel()
    {
        SampleText = "Sample";
    }
}

データバインディング

WPF + MVVM では、データバインディングという仕組みにより、View と ViewModel を結びつけるそうです。
ちなみに、コントロール同士で同期を取るだけなら XAML だけ(つまり View の部分だけ)で簡単に行えるようです。
しかし、ここでは MVVM を理解するために、ViewModel で( C# で)同期を取ります。
XAML だけで同期を取る方法はその後でやってみます。

というわけで、データバインディングにより、XAML のコントロールに ViewModel のプロパティを関連付けます。
まず、コードビハインドの以下の一行で、バインディング対象として ViewModel のクラスを指定しています。

this.DataContext = new MainWindowViewModel();

次に XAML の TextBox と Label の部分についてです。

<TextBox x:Name="textBox" HorizontalAlignment="Left" Height="23" Margin="10,10,0,0" TextWrapping="Wrap" Text="{Binding SampleText, UpdateSourceTrigger=PropertyChanged}" VerticalAlignment="Top" Width="120"/>
<Label x:Name="label" Content="{Binding SampleLabel}" HorizontalAlignment="Left" Margin="10,38,0,0" VerticalAlignment="Top"/>

TextBox の Text 属性の Binding SampleText により、TextBox とプロパティ SampleText との関連付けが行われます。
尚、UpdateSourceTrigger=PropertyChanged という記述も追加してありますが、これはまた今度説明します。
同様に、Label の Content 属性も、Binding SampleLabel とすることにより、Label とプロパティ SampleLabel との関連付けが行われます。

以上でコントロールと ViewModel のプロパティの関連付けが行われ、ViewModel で初期値として設定した文字列が View に表示されます。
しかし、これだけでは TextBox の値が変わっても Label には反映されません。
TextBox の値が変更されるとプロパティの値は変更されるのはデバッガで実行すれば確認できます。
しかし、そこで Label のプロパティを変更しても、それを View に通知する仕組みがないため、Label のコントロールの表示は変わりません。
つまり、この段階では View → ViewModel の一方向しかデータバインディングが実現できていません。

次回は双方向のデータバインディングを実現するために、プロパティの変更を View に通知する仕組みを実装します。