Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Update XAML control property value from control's code but keep binding

I have written a control with a bindable property. This control also has a method to modify that property's value:

public class MyControl : ContentView // WPF: inherited from UserControl
{
  // Xamarin:
  public static readonly BindableProperty MyValueProperty = ...
  // WPF:
  // public static readonly DependencyProperty MyValueProperty = ...

  public int MyValue
  {
     get { return (int) GetValue(MyValueProperty); }
     set { SetValue(MyValueProperty, value); }
  }

  public void Reset()
  {
    MyValue = 0;
  }
}

I am using that control in a normal XAML page and update MyValue via binding:

<local:MyControl x:Name="TheControl"
                 MyValue="{Binding MyValueSource, Mode=OneWay}" />

The binding initially propagates changes from MyValueSource to MyValue. But as soon as I call the Reset() method once, the binding is overwritten by the 0 and updates to MyValueSource are no longer pulled.

I suppose any direct assignment of MyValue is intended to replace a OneWay binding. With a TwoWay binding, the change is just propagated back to MyValueSource and the binding remains functional.

If Reset() was in the view model, I could do this:

public void Reset()
{
  // TheControl.MyValue = 0; // Bad practice, destroys the binding
  MyValueSource = 0; // Good practice, preserves the binding
}

I don't want to implement the reset logic (which is more complex than in this reduced example) in every VM though, so it's located in the view/control.

So I wonder - can you assign a bindable property's value from the control's code behind and still preserve a possible OneWay binding? I know this means the VM does not get the changed value; binding OneWay is likely not correct if the control updates the property as well; you should rather use a TwoWay binding then.

But if someone says OneWay in XAML, I'd rather have it behave that way down to the wire than implement some "OneWay until you call Reset()" behavior.

Side note: I am working in Xamarin, but I guess the behavior is the same for WPF.

like image 200
LWChris Avatar asked Jan 06 '17 11:01

LWChris


Video Answer


1 Answers

Taken and fleshed out from @Clemens' comment:

WPF

You can use the SetCurrentValue method on an DependencyObject (i. e. the control) to change the current effective value of a DependencyProperty. Unlike SetValue, with SetCurrentValue any triggers, data bindings and styles to that property remain intact.

public void Reset()
{
  // this.SetValue(MyValueProperty, 0); // Replaces the binding
  this.SetCurrentValue(MyValueProperty, 0); // Keeps the binding
}

Remember that if you defined a OneWay binding, the view model will not be notified about the changed value, and that any change to the VM's MyValueSource property will override the control's value again (if the property is implemented correctly).


Xamarin

There is currently no proper way to assign a BindableProperty's value without replacing a OneWay binding attached to it. BindableObject (the control's base class) does not have any method comparable to WPF's SetCurrentValue and SetValue will allways replace the binding.

However, if you change the binding to BindingMode.TwoWay, the internal value change is propagated back to the view model. You should probably do this anyway to keep the control and the VM synchronized.

public void Reset()
{
  // Replaces any OneWay bindings
  // Updates MyValueSource for TwoWay bindings
  this.SetValue(MyValueProperty, 0);
}
like image 153
LWChris Avatar answered Sep 30 '22 06:09

LWChris