Data binding chain

I am trying to do DataBinding

Property -> DependencyProperty -> Property

      

But I'm in trouble. For example, We have a simple class with two properties, 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));
        }
    }

      

And the TextBlock is declared in xaml:

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

      

Now try binding Num1 to 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();


        }

      

It works well. But if we uncomment this line

tb.SetBinding(TextBlock.TextProperty, binding2);

      

then the TextBlock will display nothing. DataBinding doesn't work! How can I do what I want?

+2


a source to share


1 answer


The problem is that the call SetBinding

clears up any previous bindings. So when you set the binding to Num2

, you clear the binding to Num1

. This is because a dependency property binding cannot have multiple sources - how would it know which one to use? (Of course this ignores usage MultiBinding

, but it won't help you in this scenario.)

You can do this to make dependency properties MyClass

a DependencyObject

and Num1

and Num2

. Then you can bind Num2

to a property Text

TextBox

, and it Num2

will update whenever the text gets updated from Num1

.

A picture is worth a thousand words - what you are 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 so my logic is sound and it does work, but there are some tricks. First, here's 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); }
    }
}

      

It's okay here, just replaced INotifyPropertyChanged

with DependencyProperty

. Now let's check the window code:



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're setting up two bindings. The first binds TextBlock.Text

to Num1

, the second binds Num2

to TextBlock.Text

. We now have a scenario similar to the one on the right side of the image I showed you - a data binding chain. Another magic is that we cannot update a property Num1

on a different thread from the one it was created on, which will create a cross-thread exception. To work around this, we simply call the update on the UI thread with Dispatcher

.

Finally, the XAML is used to demonstrate:

<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! Finished product:

alt text http://img163.imageshack.us/img163/6114/victorynf.png

+10


a source







All Articles