WPFプログラミング備忘録

WPF DataContext プロパティの特徴について

WPF

この記事では DataContext プロパティの強力な特徴について記載しておきます。

いきなり最終形ですが XAML ファイルは以下のとおりです。

<Window x:Class="WpfDataContextSample.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:WpfDataContextSample"
        mc:Ignorable="d"
        Title="DataContextSample" Height="280" Width="300">
    
    <Window.Resources>
        <!-- PersonCollectionクラスのインスタンス生成 -->
        <local:PersonCollection x:Key="objPersonCollection" />
    </Window.Resources>

    <Grid DataContext="{StaticResource objPersonCollection}">
        <Grid.RowDefinitions>
            <RowDefinition />
            <RowDefinition />
        </Grid.RowDefinitions>
        
        <DataGrid Grid.Row="0"
                  ItemsSource="{Binding People}"
                  AutoGenerateColumns="False" 
                  CanUserSortColumns="False" 
                  CanUserAddRows="False" 
                  SelectionMode="Single" 
                  SelectionUnit="FullRow"
                  >
            <DataGrid.Columns>
                <DataGridTextColumn Header="氏名" Binding="{Binding FullName}" Width="*" IsReadOnly="True" CanUserSort="False" />
                <DataGridTemplateColumn Header="誕生日" Width="120" CanUserSort="False">
                    <DataGridTemplateColumn.CellTemplate>
                        <DataTemplate>
                            <DatePicker SelectedDate="{Binding BirthDay, StringFormat=yyyy/MM/dd, NotifyOnSourceUpdated=True, UpdateSourceTrigger=PropertyChanged}" />
                        </DataTemplate>
                    </DataGridTemplateColumn.CellTemplate>
                </DataGridTemplateColumn>
            </DataGrid.Columns>
        </DataGrid>

        <ListBox Grid.Row="1" ItemsSource="{Binding People}" />
    </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;

namespace WpfDataContextSample
{
    public class Person
    {
        public string FullName { get; set; }
        public DateTime? BirthDay { get; set; }
        public Person(string fullName, DateTime birthDay)
        {
            FullName = fullName;
            BirthDay = birthDay;
        }
        // ListBoxに表示する各アイテムの文字列を整形するためにオーバーライドする
        public override string ToString()
        {
            return FullName + "さんの誕生日は" + BirthDay.Value.ToString("yyyy年M月d日") + "です。";
        }
    }

    public class PersonCollection
    {
        private List<Person> _people = new List<Person>();
        public List<Person> People
        {
            get { return _people; }
        }
        public PersonCollection()
        {
            _people.Add(new Person("山田 太郎", new DateTime(2002, 5, 18)));
            _people.Add(new Person("鈴木 花子", new DateTime(2004, 11, 6)));
            _people.Add(new Person("畠山 銀次", new DateTime(1982, 7, 2)));
        }
    }
    /// <summary>
    /// MainWindow.xaml の相互作用ロジック
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }
    }
}

コードビハインドでは Person クラスと PersonCollection クラスを定義しています。 PersonCollection クラスには Person クラスのコレクションとなるパブリックな List<Person> 型の People プロパティを定義しています。

XAML ファイルではリソースセクションで PersonCollection クラスをインスタンス化しています。そして Grid 要素で DataContext に PersonCollection オブジェクトを設定しています。

Grid は2行に分けられており上の行には DataGrid を配置し、下の行には ListBox を配置しています。

やりたいことは DataGrid と ListBox の両方に各 Person オブジェクトの情報を表示することです。そのためにそれぞれに ItemsSource=”{Binding People}” を指定していますが、 DataGrid と ListBox には DataContext は明示的に指定されていません。これでなぜ上手くいくのでしょうか?

実は DataContext はビジュアルツリーの中を伝搬していくという特徴を持っているため、今回の場合で言うと Grid の子要素である DataGrid と ListBox の両方に引き継がれているのです。

このプログラムの実行結果は以下のとおりとなります。

なお、ListBox の各アイテムを上図の文字列形式で表示させるために Person クラスで ToString メソッドをオーバーライドしています。

今回は DataContext プロパティはビジュアルツリーの中を伝搬していくという特徴を確認しました。

コメント