0
votes

Consider this very simple example where I have a UserControl like this:

UserControl XAML:

<UserControl x:Class="BindingTest.SomeControl"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             x:Name="SomeControlElement">
    <Grid>
        <TextBlock Text="{Binding ElementName=SomeControlElement, Path=Counter}" />
    </Grid>
</UserControl>

Code Behind:

using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Threading;
namespace BindingTest
{ 
    public partial class SomeControl : UserControl
    {
        public SomeControl()
        {
            InitializeComponent();

            var timer = new DispatcherTimer();
            timer.Interval = new TimeSpan(0, 0, 5);
            timer.Tick += (s, e) => Counter = Counter + 1;
            timer.Start();
        }

        public int Counter
        {
            get { return (int)GetValue(CounterProperty); }
            set { SetValue(CounterProperty, value); }
        }
        public static readonly DependencyProperty CounterProperty = DependencyProperty.Register(nameof(Counter), typeof(int), typeof(SomeControl), new PropertyMetadata(0));
    }
}

So the Control just shows a TextBlock and every 5 seconds increments the Counter. And then I have a consumer of course:

MainWindow XAML:

<Window x:Class="BindingTest.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:BindingTest"
        x:Name="MainWindowName" Width="200" Height="300">
    <Grid HorizontalAlignment="Center" VerticalAlignment="Center">
        <StackPanel>
            <local:SomeControl Counter="{Binding ElementName=MainWindowName, Path=SomeSource, Mode=OneWay}" />
            <local:SomeControl Counter="{Binding ElementName=MainWindowName, Path=SomeSource, Mode=TwoWay}" />
        </StackPanel>
    </Grid>
</Window>

And lastly the Main Code behind:

using System;
using System.Windows;
using System.ComponentModel;
using System.Windows.Threading;
namespace BindingTest
{
    public partial class MainWindow : Window, INotifyPropertyChanged
    {
        public MainWindow()
        {
            InitializeComponent();
            var timer = new DispatcherTimer();
            timer.Interval = new TimeSpan(0, 0, 1);
            timer.Tick += (s, e) => SomeSource = SomeSource + 1;
            timer.Start();
        }

        private int someSource;
        public int SomeSource
        {
            get => someSource;
            set
            {
                if (someSource != value)
                {
                    someSource = value;
                    PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(SomeSource)));
                }
            }
        }
        public event PropertyChangedEventHandler PropertyChanged;
    }
}

Right, so the main has a counter in code behind that updates a property every second. The XAML has 2 instances of the UserControl. One that has OneWay binding, and one that has TwoWay binding.

What I see here, is that when the Counter in "SomeControl.cs" updates, the binding for the first UserControl (OneWay) is broken. The one with TwoWay keeps on updating.

Is this by design (and why)? And more importantly, if I have the need for updating properties in my UserControls, how would I do this in my example - in order to support OneWay bindings? Mind that, I'm really not interested in TwoWay binding in this example because it would update "MySource" which is not what I wanted!

Thanks.

1
Have you tried OneWayToSource?Stefan
Also; see this one for more info: stackoverflow.com/questions/2305179/…Stefan
Thanks @Stefan, yes I've tried that. But it is by design so it seems. Using the SetCurrentValue method as mentioned in the answer, seems to solve it all.Werner

1 Answers

1
votes

It's by design. When you assign a so-called local value to a dependency property, a previously assigned OneWay Binding is replaced. A TwoWay Binding remains active and updates its source property.

There is however a workaround. Do not set a local value, but a "current value". Replace

timer.Tick += (s, e) => Counter = Counter + 1;

with

timer.Tick += (s, e) => SetCurrentValue(CounterProperty, Counter + 1);