Цепочка привязки данных - PullRequest
2 голосов
/ 04 мая 2010

Я пытаюсь следовать DataBinding

Property -> DependencyProperty -> Property

Но у меня проблема. Например, У нас есть простой класс с двумя свойствами, реализующий INotifyPropertyChanged:

public class MyClass : INotifyPropertyChanged
    {
        private string _num1;
        public string Num1
        {
            get { return _num1; }
            set
            {
                _num1 = value;
                OnPropertyChanged("Num1");
            }
        }

        private string _num2;
        public string Num2
        {
            get { return _num2; }
            set
            {
                _num2 = value;
                OnPropertyChanged("Num2");
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;

        public void OnPropertyChanged(string e)
        {
            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null) handler(this, new PropertyChangedEventArgs(e));
        }
    }

И TextBlock объявлен в xaml:

<TextBlock Name="tb" FontSize="20" Foreground="Red" Text="qwerqwerwqer" />

Теперь давайте попробуем привязать Num1 к tb.Text:

private MyClass _myClass = new MyClass();
        public MainWindow()
        {
            InitializeComponent();

            Binding binding1 = new Binding("Num1")
                                   {
                                       Source = _myClass, 
                                       Mode = BindingMode.OneWay
                                   };

            Binding binding2 = new Binding("Num2")
            {
                Source = _myClass,
                Mode = BindingMode.TwoWay
            };

            tb.SetBinding(TextBlock.TextProperty, binding1);

            //tb.SetBinding(TextBlock.TextProperty, binding2);


            var timer = new Timer(500) {Enabled = true,};

            timer.Elapsed += (sender, args) => _myClass.Num1 += "a";

            timer.Start();


        }

Работает хорошо. Но если мы раскомментируем эту строку

tb.SetBinding(TextBlock.TextProperty, binding2);

тогда TextBlock ничего не отображает. Привязка данных не работает! Как я могу делать то, что я хочу?

1 Ответ

10 голосов
/ 04 мая 2010

Проблема в том, что вызов SetBinding удаляет все предыдущие привязки. Поэтому, когда вы устанавливаете привязку на Num2, вы очищаете привязку на Num1. Это происходит потому, что привязка свойства зависимости не может иметь несколько источников - как он узнает, какой использовать? (Конечно, это игнорирует использование MultiBinding, но это не поможет вам в этом сценарии).

Способ, которым вы можете это сделать, - задать свойства зависимостей MyClass DependencyObject и Num1 и Num2. Затем вы можете привязать Num2 к свойству Text TextBox, и Num2 будет обновляться всякий раз, когда текст получает обновление от Num1.

Картинка стоит тысячи слов - то, что вы пытаетесь сделать, показано слева. То, что вам нужно сделать, показано справа:

альтернативный текст http://img339.imageshack.us/img339/448/twosources.png

Решил попробовать это, чтобы убедиться, что моя логика была правильной, и действительно, она работает, но есть некоторые хитрости. Для начала вот новый MyClass код:

public class MyClass : FrameworkElement
{
    public static readonly DependencyProperty Num1Property =
        DependencyProperty.Register("Num1", typeof(string), typeof(MyClass));

    public static readonly DependencyProperty Num2Property =
        DependencyProperty.Register("Num2", typeof(string), typeof(MyClass));

    public string Num1
    {
        get { return (string)GetValue(Num1Property); }
        set { SetValue(Num1Property, value); }
    }

    public string Num2
    {
        get { return (string)GetValue(Num2Property); }
        set { SetValue(Num2Property, value); }
    }
}

Ничего страшного здесь, просто заменили INotifyPropertyChanged на DependencyProperty. Теперь давайте проверим код окна позади:

public partial class DataBindingChain : Window
{
    public MyClass MyClass
    {
        get;
        set;
    }

    public DataBindingChain()
    {
        MyClass = new MyClass();

        InitializeComponent();

        Binding binding1 = new Binding("Num1")
        {
            Source = MyClass,
            Mode = BindingMode.OneWay
        };

        Binding binding2 = new Binding("Text")
        {
            Source = tb,
            Mode = BindingMode.OneWay
        };

        tb.SetBinding(TextBlock.TextProperty, binding1);
        MyClass.SetBinding(MyClass.Num2Property, binding2);

        var timer = new Timer(500) { Enabled = true, };

        timer.Elapsed += (sender, args) => Dispatcher.Invoke(UpdateAction, MyClass);

        timer.Start();
    }

    Action<MyClass> UpdateAction = (myClass) => { myClass.Num1 += "a"; };
}

Вот где происходит волшебство: мы установили две привязки. Первый связывает TextBlock.Text с Num1, второй связывает Num2 с TextBlock.Text. Теперь у нас есть сценарий, подобный правой стороне рисунка, который я вам показал, - цепочка привязки данных. Другая магия заключается в том, что мы не можем обновить свойство Num1 в другом потоке, отличном от того, в котором оно было создано, - это создало бы исключение между потоками. Чтобы обойти это, мы просто вызываем обновление в потоке пользовательского интерфейса, используя Dispatcher.

Наконец, XAML, используемый для демонстрации:

<Window x:Class="TestWpfApplication.DataBindingChain"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="DataBindingChain" Height="300" Width="300"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
<Grid>
    <Grid.RowDefinitions>
        <RowDefinition/>
        <RowDefinition/>
    </Grid.RowDefinitions>
    <TextBlock Name="tb" Grid.Row="0" FontSize="20" Foreground="Red"/>
    <TextBlock Name="tb2" Grid.Row="1" FontSize="20" Foreground="Blue" Text="{Binding MyClass.Num2}"/>
</Grid>

И вуаля! Готовый продукт:

альтернативный текст http://img163.imageshack.us/img163/6114/victorynf.png

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...