Why does BindablePropery immediately call set after get on a ViewModel?

jaredballenjaredballen Member ✭✭

I have property on my ViewModel that is bound to a View. When the property is updated, I would expect the BindableProperty class to call get on my property and then call set on the bound property in the View. However, I am seeing a set called on my ViewModel by the BindableProperty class, immediately following the call to get, with the same value returned from the get. Why is there an immediate call to set?

Answers

  • JamesMontemagnoJamesMontemagno USForum Administrator, Xamarin Team, Developer Group Leader Xamurai

    Do you have a source code sample?

  • jaredballenjaredballen Member ✭✭
    @JamesMontemagno thanks for the reply. I'll upload the source a bit later wen I'm in from of a computer.
  • jaredballenjaredballen Member ✭✭

    @JamesMontemagno attached is atext file with the URL to my git repo that contains the sample app I used to replicate this behavior. Sorry I can't post the actual link as I am new to the forum.

    I added an Entry on the MainPage that is bound to the Text property in MainPageViewModel. In the OnNavigatedTo method of MainPageViewModel, I added a Task with a loop to flip the value of Text every second. This operation is instrumented with debug prints as well as prints in the getter and setter for Text. In the output, I'd expect to see the value of Text set in the Task and then a call to the Text getter. I do see this but then there is an immediate call the Text setter with the same value that was set in the Task from bindable object. You'll have to break and review the call stack to see this.

  • LandLuLandLu Member, Xamarin Team Xamurai

    @jaredballen Firstly, we need to understand what is Bindable Properties. The Text property you created in your view model is not bindable property. It is used for binding to the content page's Entry's TextProperty which is the truely bindable property.

    Then when you used Text = textA ? "Text A" : "Text B"; to change the Text's value, the getter method will apparently be called. Since this property implemented the INotifyPropertyChanged interface, the page will be notified and it will retrieve that property to change its bindable property's value. Therefore, the getter method fired.

    As a result, the Entry's text changed. And you set the binding mode to TwoWay, it will also inform the view model's Text property that the Entry's bindable property has been changed. So the Text's setter method fires again.

    If you don't want the last setter to be triggered. You could set the Mode to OneWay, but this will cause your text property not being notified when the Entry's text changes.

  • jaredballenjaredballen Member ✭✭

    @LandLu Thanks for your response. I am aware of what the BindableProperty class is. I also understand that Text = textA ? "Text A" : "Text B"; will cause the set to be called on Text which will precipitate a call to get on Text. What I do not understand and why I posted this question is: Why is set called again on Text immediately following the get in response to a property changed event? If you review the debug output you will see that there is a set called and it is not the set that is called from within the Task. Reviewing the call stack during the second set call shows this.

    What is really interesting is what happens if you modify the set method so that the value that is actually assigned is never the same as the value passed. To show this can simply modify the set method to always pre-pend a character to _text. Some sort of loop ensues, where get and set are continuously called. This behavior is actually what I initially noticed and tracked down to the set always being called after a get.

  • AdamMeaneyAdamMeaney USMember ✭✭✭✭✭

    I would bet that it is difference in the implementation of the SetProperty to the BindableProperty setter.

    SetProperty equality checks the value before raising a changed event. It seems pretty likely that what you are experiencing is the Bindable Property not doing that, hence it raises a changed even though that change came from the bound object.

    In essence:

    • VMProperty = something.
    • RaiseVMPropertyChanged
    • this sets BP
    • BP RaisesPropertyChanged
    • TwoWay Binding forwards that to VM setter
    • VM Setter gets called
    • VM property already = the value, no RaisePropertyChanged.
Sign In or Register to comment.