WPF binding OneWayToSource sets the source property to "" when the DataContext changes

I have a OneWayToSource binding that does not behave as I expected when I set the DataContext of the target control. The source property is assigned a default value instead of the value of the target control property.

I created a very simple program in a standard WPF window that illustrates my problem:

Xaml

<StackPanel>
  <TextBox x:Name="tb"
    Text="{Binding Path=Text,Mode=OneWayToSource,UpdateSourceTrigger=PropertyChanged}"
    TextChanged="TextBox_TextChanged"/>

  <Button Content="Set DataContext" Click="Button1_Click"/>
</StackPanel>

MainWindow.cs

public partial class MainWindow : Window
{
   private ViewModel _vm = new ViewModel();

   private void Button1_Click(object sender, RoutedEventArgs e)
   {
      Debug.Print("'Set DataContext' button clicked");
      tb.DataContext = _vm;
   }

   private void TextBox_TextChanged(object sender, TextChangedEventArgs e)
   {
      Debug.Print("TextBox changed to " + tb.Text);
   }
}

ViewModel.cs

public class ViewModel
{
   private string _Text;
   public string Text
   {
      get { return _Text; }
      set
      {
         Debug.Print(
            "ViewModel.Text (old value=" + (_Text ?? "<null>") + 
            ", new value=" + (value ?? "<null>") + ")");
         _Text = value;
      }
   }
}

The TextBox tbstarts with a null DataContext, and therefore the binding should not do anything. Therefore, if I enter something into the text box, say "X", the ViewModel.Text property remains set to zero.

DataContext, , ViewModel.Text "X" TextBox.Text. ". , , , " Y" , "X" ViewModel.Text "XY".

( - - , , , "Y" ):

TextBox X
" DataContext"
ViewModel.Text( value = <null> , value =)
ViewModel.Text( value =, value = XY)
TextBox XY

ViewModel.Text "" "X", DataContext?

? - ? - ?

. , :

TextBox X
" DataContext"
ViewModel.Text( value = <null> , value = X)
ViewModel.Text( value = X, value = XY)
TextBox XY

+4
5

. Microsoft . x, DataContext, "Button", , TextBox x, viewModel.Text (). datacontext , getter . , .

.

+2

TextBox TextProperty, TextBox DataContext, TextBox source (viewmodel.Text), , UpdateSourceTrigger.

, viewmodel

"ViewModel.Text (old value=<null>, new value=)"

UpdateSourceTrigger=PropertyChanged.

init:

private string _Text;
public string Text
{
    get { return _Text; }
    set
    {
        Debug.Print(
           "ViewModel.Text (old value=" + (_Text ?? "<null>") +
           ", new value=" + (value ?? "<null>") + ")");
        _Text = value;
    }
}

UpdateSourceTrigger=PropertyChanged, TextBox.Text.

"Y", PropertyChanged, viewmodel TextBox.

0

UpdateSource, :

 private void Button1_Click(object sender, RoutedEventArgs e)
   {

      Debug.Print("'Set DataContext' button clicked");
      tb.DataContext = _vm;
      var bindingExp = tb.GetBindingExpression(TextBox.TextProperty);
      bingExp.UpdateSource();
   }
0

.NET 4 , getter OneWayToSource, . , tb.DataContext = _vm; , setter getter Text. , viewmodel datacontext..NET 4.5 . .

private void Button1_Click(object sender, RoutedEventArgs e)
{
   Debug.Print("'Set DataContext' button clicked");       
    _vm.Text=tb.Text;
    tb.DataContext = _vm;
}
0

Attached property:

public static readonly DependencyProperty OneWaySourceRaiseProperty = DependencyProperty.RegisterAttached("OneWaySourceRaise", typeof(object), typeof(FrameworkElementExtended), new FrameworkPropertyMetadata(OneWaySourceRaiseChanged));

        public static object GetOneWaySourceRaise(DependencyObject o)
        {
            return o.GetValue(OneWaySourceRaiseProperty);
        }

        public static void SetOneWaySourceRaise(DependencyObject o, object value)
        {
            o.SetValue(OneWaySourceRaiseProperty, value);
        }

        private static void OneWaySourceRaiseChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            if (e.NewValue == null)
                return;

            var target = (FrameworkElement)d;
            target.Dispatcher.InvokeAsync(() =>
        {
            var bindings = target.GetBindings().Where(i => i.ParentBinding?.Mode == BindingMode.OneWayToSource).ToArray();
            foreach (var i in bindings)
            {
                i.DataItem.SetProperty(i.ParentBinding.Path.Path, d.GetValue(i.TargetProperty));
            }
        });

XAML:

extendends:FrameworkElementExtended.OneWaySourceRaise="{Binding}"

{Binding} - DataContext. :

    public static IEnumerable<BindingExpression> GetBindings<T>(this T element, Func<DependencyProperty, bool> func = null) where T : DependencyObject
            {
                var properties = element.GetType().GetDependencyProperties();
                foreach (var i in properties)
                {
                    var binding = BindingOperations.GetBindingExpression(element, i);
                    if (binding == null)
                        continue;
                    yield return binding;
                }
            }


private static readonly ConcurrentDictionary<Type, DependencyProperty[]> DependencyProperties = new ConcurrentDictionary<Type, DependencyProperty[]>();
    public static DependencyProperty[] GetDependencyProperties(this Type type)
            {
                return DependencyProperties.GetOrAdd(type, t =>
                {
                    var properties = GetDependencyProperties(TypeDescriptor.GetProperties(type, new Attribute[] { new PropertyFilterAttribute(PropertyFilterOptions.All) }));
                    return properties.ToArray();
                });
            }

            private static IEnumerable<DependencyProperty> GetDependencyProperties(PropertyDescriptorCollection collection)
            {
                if (collection == null)
                    yield break;
                foreach (PropertyDescriptor i in collection)
                {
                    var dpd = DependencyPropertyDescriptor.FromProperty(i);
                    if (dpd == null)
                        continue;
                    yield return dpd.DependencyProperty;
                }
            }
0

Source: https://habr.com/ru/post/1545916/


All Articles