Настройка пользовательских свойств в UserControl через DataBinding
Скажем, у меня очень простой UserControl, который - для всех целей и задач - это не что иное, как TextBox:
public partial class FooBox : UserControl
{
public static readonly DependencyProperty FooTextProperty =
DependencyProperty.Register("FooText", typeof(string), typeof(FooBox));
public FooBox()
{
InitializeComponent();
}
public string FooText
{
get { return textBlock.Text; }
set { textBlock.Text = value; }
}
}
<UserControl x:Class="Namespace.FooBox"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Height="300" Width="300">
<Grid>
<TextBlock x:Name="textBlock" />
</Grid>
</UserControl>
В форме, которую она объявила как:
<local:FooBox FooText="{Binding Name}" />
Форма DataContext имеет объект, обладающий свойством Name. Но это не работает для меня. Что мне не хватает?
Ответы
Ответ 1
Оказывается, реальная проблема заключается в том, что я ожидал, что среда WPF установит мое публичное свойство, после чего мой код будет реагировать на изменения и рендерить в соответствии с новым значением. Не так. Что делает WPF, это вызов SetValue() напрямую и полностью обходит публичную собственность. Мне нужно было получать уведомления об изменении свойств с помощью DependencyPropertyDescriptor.AddValueChanged и отвечать на это. Он выглядит примерно так (внутри ctor):
var dpd = DependencyPropertyDescriptor
.FromProperty(MyDependencyProperty, typeof(MyClass));
dpd.AddValueChanged(this, (sender, args) =>
{
// Do my updating.
});
Ответ 2
"get" и "set" части объявления свойства в DependencyProperty фактически не вызываются системой привязки данных WPF - они там, по существу, удовлетворяют только компилятору.
Вместо этого измените объявление свойства, чтобы оно выглядело следующим образом:
public string FooText
{
get { return (string)GetValue(FooTextProperty); }
set { SetValue(FooTextProperty, value); }
}
... и ваш XAML:
<UserControl ...
x:Name="me">
<Grid>
<TextBlock Text="{Binding FooText,ElementName=me}" />
</Grid>
</UserControl>
Теперь ваш TextBox.Text просто привязывается непосредственно к свойству "FooText", поэтому вы можете привязать свойство FooText к "Name" так же, как в настоящее время.
Другим способом является привязка TextBlock.Text к привязке RelativeSource, которая находит свойство FooText для первого предка типа "FooBox", но я обнаружил, что это сложнее, чем просто дать управление внутреннему x: Name и используя привязку элементов.