WPFプログラミング備忘録

WPF データバインディングとUIへの通知機能

WPF

この記事ではデータバインディングとUIへの通知機能について確認します。

データバインディングの規則

データバインディングにはバインディングターゲットとバインディングソースがあります。

バインディングソースは.NETの通常のCLRプロパティあるいは依存関係プロパティで構いませんが、バインディングターゲットは依存関係プロパティでなければいけません。

CLRプロパティ

ここではバインディングソースとしてCLRプロパティを使用します。

下図のアプリケーションでは名前を入力して挨拶ボタンを押すとダイアログボックスでメッセージを表示し、リセットボタンを押すと名前を消去することを想定しています。

XAMLファイルとコードビハインドは以下のとおりです。

<Window x:Class="WpfDependencyProperty.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:WpfDependencyProperty"
        mc:Ignorable="d"
        x:Name="mainWindow"
        Title="MainWindow" Height="150" Width="300">
    <Grid Margin="10">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto"/>
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>
        <TextBlock Text="名前 " Grid.Row="0" Grid.Column="0" />
        <TextBox Text="{Binding YourName, ElementName=mainWindow}" Grid.Row="0" Grid.Column="1" />
        <StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2">
            <Button Content="挨拶" Width="80" Margin="8" Click="OnSubmit" />
            <Button Content="リセット" Width="80" Margin="8" Click="OnReset" />
        </StackPanel>
    </Grid>
</Window>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.ComponentModel;
using System.Runtime.CompilerServices;

namespace WpfDependencyProperty
{
    /// <summary>
    /// MainWindow.xaml の相互作用ロジック
    /// </summary>
    public partial class MainWindow : Window
    {
        // CLRプロパティ
        public string YourName { get; set; }

        public MainWindow()
        {
            InitializeComponent();
        }

        private void OnSubmit(object sender, RoutedEventArgs e)
        {
            MessageBox.Show(YourName + "さん、こんにちは。");
        }

        private void OnReset(object sender, RoutedEventArgs e)
        {
            YourName = string.Empty;
        }
    }
}

山田太郎と入力して挨拶ボタンを押すとダイアログボックスが表示されることがわかります。

しかしリセットボタンを押しても名前が消去されません。バインディングソースであるYourNameプロパティ(CLRプロパティ)の値をプログラムコードで消去していますがUIへは反映されていません。

これはCLRプロパティには値が変更されてもUIへ自動的に通知するメカニズムが備わっていないためです。

以下で2つの対処方法を確認します。

INotifyPropertyChangedインターフェース

1つ目の方法はINotifyPropertyChangedインターフェースを実装することです。

INotifyPropertyChangedインターフェースを実装したコードビハインドは以下のとおりです。XAMLファイルに変更はありません。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.ComponentModel;
using System.Runtime.CompilerServices;

namespace WpfDependencyProperty
{
    /// <summary>
    /// MainWindow.xaml の相互作用ロジック
    /// </summary>
    public partial class MainWindow : Window, INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        private void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        private string yourName;
        // CLRプロパティ
        public string YourName
        {
            get { return yourName; }
            set
            {
                if (value != yourName)
                {
                    yourName = value;
                    NotifyPropertyChanged();
                }
            }
        }

        public MainWindow()
        {
            InitializeComponent();
        }

        private void OnSubmit(object sender, RoutedEventArgs e)
        {
            MessageBox.Show(YourName + "さん、こんにちは。");
        }

        private void OnReset(object sender, RoutedEventArgs e)
        {
            YourName = string.Empty;
        }
    }
}

書き方はいくつかありますが上記はオーソドックスな方法です。

プログラムを実行して名前を入力後にリセットボタンを押すと消去されることがわかります。

大事なことはCLRプロパティに変更があった時はUIへ明示的に通知する必要があるということです。

依存関係プロパティ

2つ目の方法は依存関係プロパティを使用することです。

YourNameプロパティを依存関係プロパティに変更したコードビハインドは以下のとおりです。 XAMLファイルに変更はありません。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.ComponentModel;
using System.Runtime.CompilerServices;

namespace WpfDependencyProperty
{
    /// <summary>
    /// MainWindow.xaml の相互作用ロジック
    /// </summary>
    public partial class MainWindow : Window
    {
        // 依存関係プロパティ
        public string YourName
        {
            get { return (string)GetValue(YourNameProperty); }
            set { SetValue(YourNameProperty, value); }
        }

        public static readonly DependencyProperty YourNameProperty =
            DependencyProperty.Register("YourName", typeof(string), typeof(MainWindow), new PropertyMetadata(string.Empty));


        public MainWindow()
        {
            InitializeComponent();
        }

        private void OnSubmit(object sender, RoutedEventArgs e)
        {
            MessageBox.Show(YourName + "さん、こんにちは。");
        }

        private void OnReset(object sender, RoutedEventArgs e)
        {
            YourName = string.Empty;
        }
    }
}

これもお決まりの書き方です。依存関係プロパティはpropdpと入力してTabキーを2回押すと雛形が作成されます。

依存関係プロパティはCLRプロパティと違い、値に変更があった場合UIへ自動的に通知するメカニズムを備えています。

再度プログラムを実行して名前を入力後にリセットボタンを押すと消去されることがわかります。

依存関係プロパティのメリットの1つは煩わしいINotifyPropertyChangedインターフェースを実装しなくてもよいことです。

ただし、カスタムクラスなどでこの方法を採用すると、DependencyObjectクラスあるいはその派生クラスを継承する必要があります。C#では単一継承しか許されていないため、他のクラスを継承できなくなることを考えると、この方法はお勧めできません。

なおデータバインディングには方向を指定できます。これについては以下の記事で確認しています。

今回はデータバインディングとUIへの通知機能について確認しました。

以上です。

コメント