UserControlに実装したプロパティをBindingSourceにする
About
UserControlを利用して独自のコントロールを開発することは少なくありません。UserControlに独自のプロパティを実装し、その外観(コントロール)にプロパティの値を反映したい、ということも良くあります。プロパティの値が変更されたタイミングで、コントロール側の値を任意に変更することで実装できないこともないですが、スマートではありません。折角WPFを利用するのだから、Bindingを利用したいところです。
しかしながらBindingする方法は多数あり、Binding方法によっては不具合を来す可能性があります。そこでBindingに関して苦戦したことをここにメモしていきます。
- &ref(): File not found: "WpfApplication1_BindingDependencyProperty.zip" at page "Programming/.NetFramework/WPF/Binding/BindingDependencyPropertyInUserControl";
- VisualStduio2012
- WPF
ここで扱うサンプル
ここで扱うサンプルにはもっとも単純なパターンを用意しました。あるUserControlにLabelとその他のコントロール(ここではGrid)を配置します。そのLabelのプロパティContentを、UserControlに実装したLabelプロパティから設定できるようにします。
LabelプロパティはBindingのために依存プロパティ(DependencyProperty)として用意します。依存プロパティの解説についてはここでは行いません。
<UserControl x:Class="WpfApplication1_BindingDependencyProperty.UserControl1" … d:DesignHeight="300" d:DesignWidth="300" Background="White"> <Grid> <Grid.RowDefinitions> <RowDefinition Height="Auto"/> <RowDefinition /> </Grid.RowDefinitions> <Label Height="50" Content="プロパティの値"/> <Grid Background="Blue" Grid.Row="1"/> </Grid> </UserControl>
public static readonly DependencyProperty LabelProperty = DependencyProperty.Register("Label", typeof(object), typeof(UserControl1), new UIPropertyMetadata("Label")); [BindableAttribute(true)] public object Label { get { return (object)GetValue(LabelProperty); } set { SetValue(LabelProperty, value); } }
HowTo?
RelativeSource AncestorTypeを利用する方法
残念ながら現状ではこれ以上の良い方法を見つけ出すことはできませんでした。あまり効率の良い実装方法には見えませんが、問題を起こしにくい実装形態であると思います。
RelativeSourceは動的に変更される可能性のある値をBindingに利用するための仕組みを提供しています。この仕組みの内、AncestorTypeを利用して、自分の要素(ここではLabel)を内包する、親の要素を対象に指定します。対象の指定の方法にはいくつかありますが、ここではTypeによる指定を利用します。Bindingしたい独自の依存プロパティ(DependencyProperty)はUserControlに実装しているわけですから、Typeの指定先はUserControlとします。
<Label Content="{Binding Label, RelativeSource={RelativeSource AncestorType={x:Type UserControl} } }"/>
もしも自分を内包する親の要素に複数のUserControlが存在し、その内のいずれかのUserControlを任意に設定する必要があるときは、AncestorLevelを指定します。
Nameを利用する
基本的には良くない方法、のハズなのですが、良くない理由を失念しました。実装したUserControlに何かしらのパラメータをBindingする際に不都合が出た記憶があります。
プロパティが実装されるUserControlに適当な名前をつけてBindingに利用します。ここではx:Name="Root"としました。名前を付けたらBindingする方から参照に利用します。
<UserControl x:Class="WpfApplication1_BindingDependencyProperty.UserControl2" … x:Name="Root"> <Grid> … <Label x:Name="label" Height="50" Content="{Binding ElementName=Root, Path=Label}"/> <Grid Background="Blue" Grid.Row="1"/> </Grid> </UserControl>
DataContextを利用する方法
DataContextを利用して設定することもできなくはないです。しかしながら基本的には良くない方法です。なぜならば、UserControlのDataContextはそれを利用する側に変更されてしまう可能性があるからです。DataContextを変更することによってコントロールにデータをBindingすることは良くあることです。
DataContextが設定されている場合には、DataContextが持つプロパティの値をBindingすることができます。ここでは自分自身のプロパティを参照することができるので、任意に設定したDependencyProperty、Labelが利用できます。
<UserControl x:Class="WpfApplication1_BindingDependencyProperty.UserControl3" … DataContext="{Binding RelativeSource={RelativeSource Self} }"> <Grid> … <Label x:Name="label" Height="50" Content="{Binding Label}"/> <Grid Background="Blue" Grid.Row="1"/> </Grid> </UserControl>
XAMLではなくコード側(ビハインド)からDataContextを設定することも考えられます。
//コンストラクタ public UserControl1() { //DataContextの設定 this.DataContext = this; }