I am trying to do follow DataBinding
Property -> DependencyProperty -> Property
But i have trouble. For example, We have simple clas
The problem is that the SetBinding
call clears out any previous bindings. So when you set a binding to Num2
, you are clearing out the binding to Num1
. This happens because a dependency property binding cannot have multiple sources- how would it know which one to use? (Of course, this ignores the usage of a MultiBinding
, but that's not going to help you in this scenario).
The way you can do this is to make MyClass
a DependencyObject
and Num1
and Num2
dependency properties. Then you can bind Num2
to the Text
property of the TextBox
, and Num2
will be updated whenever the text receives an update from Num1
.
A picture is worth a thousand words- what you're trying to do is shown on the left. What you need to do is shown on the right:
alt text http://img339.imageshack.us/img339/448/twosources.png
Decided to try this out to ensure my logic was sound, and indeed it works, but there are some tricks. For starters, here is the new MyClass
code:
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); }
}
}
Nothing scary here, just replaced your INotifyPropertyChanged
with DependencyProperty
. Now let's check out the window code-behind:
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"; };
}
This is where the magic happens: we set up two bindings. The first binds the TextBlock.Text
to Num1
, the second binds Num2
to the TextBlock.Text
. Now we have a scenario like the right side of the picture I showed you- a data-binding chain. The other magic is that we cannot update the Num1
property on a different thread from the one it was created on- that would create a cross-thread exception. To bypass this, we simply invoke an update onto the UI thread using the Dispatcher
.
Finally, the XAML used for demonstration:
<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>
And voila! The finished product:
alt text http://img163.imageshack.us/img163/6114/victorynf.png