for WPF developers
データバインド機能やアニメーション機能などを提供できるようにするには、 通常の CLR プロパティではなく、依存関係プロパティを定義する必要があります。 ここでは、ユーザコントロールに対して依存関係プロパティを独自に作成し、 そのプロパティに対してデータバインドをおこなう例を紹介します。
サンプルプロジェクトはここからダウンロードできます。
ページ内リンク
例えば次のような画面を作ることを考えます。 項目名が左にあって、それに対する設定値が右側にあるという、 設定画面なんかでよく見るレイアウトです。 これを XAML で書こうとすると次のようになります。
ただ縦並びにするだけなら StackPanel でもいいと思われますが、 左側に表示している設定項目名のテキストを ":" で綺麗に揃えたい場合は上記のように Grid で綺麗に並べる必要があります。
しかし、このような方法を取ると、 設定項目数に応じて RowDefinitions の設定を変更しなくてはいけません。 しかも、TextBlock や TextBox などにいちいち Grid の添付プロパティを設定しなければならないため、 XAML に記述する量が多くなり、 単純な UI を実現しているのにコードが長い&複雑になってしまいます。
これを解消するために、次のようなユーザコントロールを使用することを考えます。
ユーザコントロール名を ConfigurationItem として次のようなコントロールを作成します。
設定項目名を左側に、設定値を入力するための TextBox を右側に配置したユーザコントロールです。 これを MainView で並べてみます。
XAML コードのほうは、設定項目の分だけ StackPanel で並べるだけなので、非常に簡潔になりました。
しかし、このままでは設定項目名はすべて "項目名" で、設定する値も受け渡すことができません。 そこで、項目名や設定値をデータバインド機能を用いて指定するようにするために、 依存関係プロパティを追加します。
まず、項目名を外部から指定できるようにユーザコントロールに対して "Text" という名前の依存関係プロパティを追加します。 ConfigurationItem のコードビハインドに次のように追加します。 DependencyProperty クラスの Register() 静的メソッドによって DependencyProerty クラスを生成します。 このとき、依存関係プロパティの名前、その型、どの型に属するのか、その他オプションを引数として与えます。 ここでは、名前は "Text"、型は typeof(string)、Text プロパティは ConfigurationItem クラスに属しているので typeof(ConfigurationItem)、 オプションとしてメタデータを指定し、このプロパティの既定値を "項目名" として与えています。
続いて、依存関係プロパティを XAML 上から扱えるように、通常の CLR プロパティで GetValue() や SetValue() をラップしています。
この状態で一度ビルドすると、Text という依存関係プロパティが生成されたので、 XAML 上でコードを記述するときに、Intellisense 機能で Text というプロパティがヒットします。
次に、設定値もデータバインドできるように "Value" という名前の依存関係プロパティを追加します。 先ほどの Text プロパティを追加したときとほとんど同じですが、 ここで注意してほしいのは、 メタデータの中で FrameworkPropertyMetadataOptions.BindsTwoWayByDefault を指定しているところです。 依存関係プロパティを追加した場合、デフォルトではデータバインド機能は OneWay となります。 しかし、この Value プロパティは UI からの入力によって値が変更され、 この変更をデータバインド機能によって外部に通知しようとしています。 したがって、データバインドは TwoWay でバインドされるべきです。 このことから、データバインド機能を TwoWay として依存関係プロパティを定義する必要がありますが、 これをメタデータから変更しています。
Text プロパティと Value プロパティを定義したので、 定義したプロパティを参照してそれぞれを表示するようにユーザコントロールの XAML を書き換えます。 TextBlock の Text プロパティに追加した Text プロパティ、 TextBox の Text プロパティに追加した Value プロパティを参照するようにしています。 ただし、参照先は DataContext ではなく、ユーザコントロール自身であるため、 ElementName を指定しなければならないことに注意して下さい。 ElementName を指定しないと、ユーザコントロールの DataContext が参照先になりますが、 DataContext はユーザコントロールを配置する View の DataContext となってしまい、 そちらの Text プロパティや Value プロパティが参照されることになってしまいます。 ここではユーザコントロール自身に作成した Text プロパティと Value プロパティを参照します。
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 で見えるようにしています。
実行結果は次のようになります。 TextBox の値を変更すると WholeText プロパティまでその変更が伝わっていることがわかります。
Designed by CSS.Design Sample