Проблема в том, что вызов 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