for WPF developers
データバインド機能やアニメーション機能などを提供できるようにするには、 通常の CLR プロパティではなく、依存関係プロパティを定義する必要があります。 ここでは、ユーザコントロールに対して依存関係プロパティを独自に作成し、 そのプロパティに対してデータバインドをおこなう例を紹介します。
サンプルプロジェクトはここからダウンロードできます。
ページ内リンク
例えば次のような画面を作ることを考えます。
<Window x:Class="CustomDependencyProperty.Views.MainView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainView" Height="300" Width="300">
<Grid HorizontalAlignment="Center" VerticalAlignment="Center">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="100" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Row="0" Grid.Column="0" Text="設定 1 : " TextAlignment="Right" VerticalAlignment="Center" />
<TextBlock Grid.Row="1" Grid.Column="0" Text="詳細設定 : " TextAlignment="Right" VerticalAlignment="Center" />
<TextBlock Grid.Row="2" Grid.Column="0" Text="オプション : " TextAlignment="Right" VerticalAlignment="Center" />
<TextBox Grid.Row="0" Grid.Column="1" Margin="0,0,0,6" />
<TextBox Grid.Row="1" Grid.Column="1" Margin="0,0,0,6" />
<TextBox Grid.Row="2" Grid.Column="1" Margin="0,0,0,6" />
</Grid>
</Window>
ただ縦並びにするだけなら StackPanel でもいいと思われますが、 左側に表示している設定項目名のテキストを ":" で綺麗に揃えたい場合は上記のように Grid で綺麗に並べる必要があります。
しかし、このような方法を取ると、 設定項目数に応じて RowDefinitions の設定を変更しなくてはいけません。 しかも、TextBlock や TextBox などにいちいち Grid の添付プロパティを設定しなければならないため、 XAML に記述する量が多くなり、 単純な UI を実現しているのにコードが長い&複雑になってしまいます。
これを解消するために、次のようなユーザコントロールを使用することを考えます。
ユーザコントロール名を ConfigurationItem として次のようなコントロールを作成します。
<UserControl x:Class="CustomDependencyProperty.Views.ConfigurationItem"
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"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid Margin="0,0,0,6">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="項目名 : " TextAlignment="Right" VerticalAlignment="Center" />
<TextBox Grid.Column="1" Width="100" />
</Grid>
</UserControl>
設定項目名を左側に、設定値を入力するための TextBox を右側に配置したユーザコントロールです。
これを MainView で並べてみます。
<Window x:Class="CustomDependencyProperty.Views.MainView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:CustomDependencyProperty.Views"
Title="MainView" Height="300" Width="300">
<Grid>
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
<local:ConfigurationItem />
<local:ConfigurationItem />
<local:ConfigurationItem />
</StackPanel>
</Grid>
</Window>
XAML コードのほうは、設定項目の分だけ StackPanel で並べるだけなので、非常に簡潔になりました。
しかし、このままでは設定項目名はすべて "項目名" で、設定する値も受け渡すことができません。 そこで、項目名や設定値をデータバインド機能を用いて指定するようにするために、 依存関係プロパティを追加します。
まず、項目名を外部から指定できるようにユーザコントロールに対して "Text" という名前の依存関係プロパティを追加します。
ConfigurationItem のコードビハインドに次のように追加します。
namespace CustomDependencyProperty.Views
{
using System.Windows;
using System.Windows.Controls;
/// <summary>
/// ConfigurationItem.xaml の相互作用ロジック
/// </summary>
public partial class ConfigurationItem : UserControl
{
public ConfigurationItem()
{
InitializeComponent();
}
public static readonly DependencyProperty TextProperty = DependencyProperty.Register("Text", typeof(string), typeof(ConfigurationItem), new FrameworkPropertyMetadata("設定項目"));
public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
}
}
続いて、依存関係プロパティを XAML 上から扱えるように、通常の CLR プロパティで GetValue() や SetValue() をラップしています。
この状態で一度ビルドすると、Text という依存関係プロパティが生成されたので、
XAML 上でコードを記述するときに、Intellisense 機能で Text というプロパティがヒットします。
次に、設定値もデータバインドできるように "Value" という名前の依存関係プロパティを追加します。
public static readonly DependencyProperty ValueProperty = DependencyProperty.Register("Value", typeof(string), typeof(ConfigurationItem), new FrameworkPropertyMetadata(string.Empty, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
public string Value
{
get { return (string)GetValue(ValueProperty); }
set { SetValue(ValueProperty, value); }
}
Text プロパティと Value プロパティを定義したので、
定義したプロパティを参照してそれぞれを表示するようにユーザコントロールの XAML を書き換えます。
<UserControl x:Class="CustomDependencyProperty.Views.ConfigurationItem"
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"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300"
x:Name="root">
<Grid Margin="0,0,0,6">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="{Binding Text, ElementName=root, StringFormat='{}{0} : '}" TextAlignment="Right" VerticalAlignment="Center" />
<TextBox Grid.Column="1" Text="{Binding Value, ElementName=root}" Width="100" />
</Grid>
</UserControl>
StringFormat は表示するテキストの様式を指定できるオプションで、 ここでは設定項目名の後ろに " : " を付けるようにしています。
それではこのユーザコントロールを使用する MainView に戻って、
それぞれのプロパティを設定してみましょう。
<Window x:Class="CustomDependencyProperty.Views.MainView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:CustomDependencyProperty.Views"
Title="MainView" Height="300" Width="300">
<Grid>
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
<local:ConfigurationItem Text="設定 1" Value="{Binding Text1}" />
<local:ConfigurationItem Text="詳細設定" Value="{Binding Text2}" />
<local:ConfigurationItem Text="オプション" Value="{Binding Text3}" />
<TextBlock Text="{Binding WholeText}" />
</StackPanel>
</Grid>
</Window>
namespace CustomDependencyProperty.ViewModels
{
public class MainViewModel : NotificationObject
{
private string text1;
public string Text1
{
get { return text1; }
set { SetProperty(ref text1, value); }
}
private string text2;
public string Text2
{
get { return text2; }
set { SetProperty(ref text2, value); }
}
private string text3;
public string Text3
{
get { return text3; }
set { SetProperty(ref text3, value); }
}
public string WholeText
{
get { return Text1 + Text2 + Text3; }
}
}
}
また、ここではプロパティ値が変更されたことを確認するために WholeText プロパティを用意し、 View 側ではそれを TextBlock で見えるようにしています。
実行結果は次のようになります。
Designed by CSS.Design Sample