泥庭

2014年12月11日

Bindingを整理しよう

Filed under: .NET, WPF — タグ: — yone64 @ 11:08 AM

この記事は、XAML AdventCalendarの11日目の記事です。

皆さんがハードル上げまくってますが、ちょっと基本に立ち返り、XAMLと切っても切れない縁のBindingを整理してみたいと思います。
とはいえ、Bindingだけでも範囲がめっちゃ広いので、どこから値を持ってくるか?という点に絞ります。

基本編

BindingはFrameworkElement.DataContextプロパティーに設定されたオブジェクトを参照します。

/// <summary>
/// MainWindow.xaml の相互作用ロジック
/// </summary>
public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();

        this.DataContext = "Hello World!";
    }
}
<Window x:Class="BindingSamples.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <Viewbox>
            <TextBlock Text="{Binding}" />
        </Viewbox>
    </Grid>
</Window>

image
DataContextに設定した文字が、TextBlockのTextとして表示されました。

Path色々編

通常はDataContextに設定したオブジェクトを他のプロパティーに設定しなおすことはほぼなく、オブジェクトのプロパティーを参照することが多いです。

/// <summary>
/// MainWindow.xaml の相互作用ロジック
/// </summary>
public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();

        this.DataContext = new
        {
            Text = "Hello World",
            Hoge = new { Fuga = "PIYO"},
            Collection = new[] { "One", "Two", "Three" }
        };
    }
}
<Window x:Class="BindingSamples.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <Viewbox>
            <StackPanel Orientation="Vertical">
                <TextBlock Text="{Binding Text}" />
                <TextBlock Text="{Binding Hoge.Fuga}" />
                <TextBlock Text="{Binding Collection[1]}" />
            </StackPanel>
        </Viewbox>
    </Grid>
</Window>

image
Property名を指定することで、そのオブジェクトのPropertyの値を取得することができます。
また、「.」でプロパティー名を連結することで、階層的に値をたどっていくことも可能です。その他、[ ]を使いIndexerでのアクセスも可能です。
なお、Pathの指定は先頭に限り省略可能で、{Binding Text}と{Biding Path=Text}は同じ意味になります。
「/」を利用することにより、選択されている~といった指定も可能ですが、ここでは割愛。詳しくは↓を参照ください。
http://msdn.microsoft.com/ja-jp/library/ms742451(v=vs.110).aspx

異なるソース編

ここまでのBindingはDataContextプロパティーのオブジェクトを参照していましたが、異なるソースを指定することもできます。

<Window x:Class="BindingSamples.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Grid x:Name="Grid">
        <Viewbox>
            <StackPanel Orientation="Vertical">
                <TextBlock Text="{Binding Path=MainWindow.Title, Source={x:Static Application.Current}}" />
                <TextBlock Text="{Binding Path=ActualWidth, ElementName=Grid}" />
            </StackPanel>
        </Viewbox>
    </Grid>
</Window>

image

Sourceプロパティーを指定することで、他のオブジェクトを参照することができます。ここには、{x:Static} のほかに{StaticResource}などもよく使います。
また、ElementNameを指定すると、XAML上の他のオブジェクトへの参照を取得することができます。ここでは、最上位のGridが取得できます。

異なるソース編 その2

自分自身から相対参照をもって値を取得することもできます。

<Window x:Class="BindingSamples.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Grid x:Name="Grid">
        <Viewbox>
            <Grid>
                <StackPanel Orientation="Vertical">
                    <TextBlock Text="{Binding Path=FontFamily, RelativeSource={RelativeSource Self}}" />
                    <TextBlock Text="{Binding Path=Children.Count, RelativeSource={RelativeSource AncestorType={x:Type Grid}}}" />
                    <TextBlock Text="{Binding Path=Children.Count, RelativeSource={RelativeSource AncestorType={x:Type Grid} ,AncestorLevel=2}}" />
                </StackPanel>
            </Grid>
        </Viewbox>
        <Grid/>
    </Grid>
</Window>

image

Selfは文字通り、自分自身ですね。ここでは、TextBlock自身。
AncestorTypeは、FindAncestor(省略可能)モードの場合に使うキーワードで、XAMLをさかのぼって指定された型のオブジェクトを見つけ、参照します。これにAncestorLevelも追加すると見つけた2つ目の~みたいな指定ができるようになります。
あと、RelativeSourceでは特別な場合のみに使える指定があります。

<Window x:Class="BindingSamples.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Window.Resources>
        <ControlTemplate x:Key="Template" TargetType="{x:Type Button}">
            <TextBlock Text="{Binding Path=Foreground,RelativeSource={RelativeSource TemplatedParent}}"/>
        </ControlTemplate>
    </Window.Resources>
    <Grid x:Name="Grid">
        <Viewbox>
            <StackPanel Orientation="Vertical">
                <Button Foreground="Red" Template="{StaticResource Template}" />
                <Button Foreground="Blue" Template="{StaticResource Template}"/>
            </StackPanel>
        </Viewbox>
    </Grid>
</Window>

image

まずは、Templateの内部でのみ使えるTemplateParent。文字通りTemplateを保持する親コントロールへの参照を取得します。

<Window x:Class="BindingSamples.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Grid x:Name="Grid">
        <Viewbox>
            <ItemsControl ItemsSource="{Binding Collection}">
                <ItemsControl.ItemTemplate>
                    <DataTemplate>
                        <Border BorderThickness="1" BorderBrush="Black">
		                    <Grid>
		                        <Grid.ColumnDefinitions>
		                            <ColumnDefinition />
		                            <ColumnDefinition />
		                        </Grid.ColumnDefinitions>
		                        <Grid.RowDefinitions>
		                            <RowDefinition />
		                            <RowDefinition />
		                        </Grid.RowDefinitions>
		                        <TextBlock Text="今"/>
		                        <TextBlock Grid.Row="1" Text="前"/>

		                        <TextBlock Grid.Column="1" Text="{Binding}" />
		                        <TextBlock Grid.Column="1" Grid.Row="1" Text="{Binding RelativeSource={RelativeSource PreviousData}}" />
		                    </Grid>
                        </Border>
                    </DataTemplate>
                </ItemsControl.ItemTemplate>
            </ItemsControl>
        </Viewbox>
    </Grid>
</Window>

image
あとは、ItemsControl系のDataTemplate内部でのみ使用できるPreviousData。これも文字通りで、ItemsSourceに指定されたCollectionのひとつ前の情報を取得できます。
# しかし、実際に使ったことはないのですよ。(どこで使うんだろう。。。)
というわけで、Bindingは結構柔軟にあちこちからデータを取得することができます。ぜひ活用してください。

広告

コメントする »

まだコメントはありません。

RSS feed for comments on this post. TrackBack URI

コメントを残す

以下に詳細を記入するか、アイコンをクリックしてログインしてください。

WordPress.com ロゴ

WordPress.com アカウントを使ってコメントしています。 ログアウト / 変更 )

Twitter 画像

Twitter アカウントを使ってコメントしています。 ログアウト / 変更 )

Facebook の写真

Facebook アカウントを使ってコメントしています。 ログアウト / 変更 )

Google+ フォト

Google+ アカウントを使ってコメントしています。 ログアウト / 変更 )

%s と連携中

WordPress.com Blog.

%d人のブロガーが「いいね」をつけました。