Подтвердить что ты не робот

Настройка пользовательских свойств в 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. Но это не работает для меня. Что мне не хватает?

4b9b3361

Ответ 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 и используя привязку элементов.