Hi,
I have a binding issue in my Xamarin.Forms project with the following setup:
GenericPage
superclass inheriting from ContentPage
and containing a BindableProperty
Page
class inheriting from GenericPage
with a ViewModel
which is bound to the BindableProperty
using a OneWayToSource
binding modeGenericControl
superclass inheriting from ContentView
and containing a BindableProperty
Control
class inheriting from GenericControl
with a ControlViewModel
which is bound to the BindableProperty
using a OneWayToSource
binding modeControl
class is embedded in the Page
class using XAML and the BindableProperty
of GenericControl
is bound to the property of the ViewModel
using a OneWay
binding modeVisual representation of this setup:
I can verify that the "connection" from Page
to the BindableProperty
of GenericControl
indeed works, as the propertyChanged
method is invoked in GenericControl
with the correct value from the BindableProperty
in GenericPage
. I can also verify that the "connection" from GenericControl
to ControlViewModel
is working (at least in the beginning) as the property setter in ControlViewModel
is called once with the default value from the BindableProperty
in GenericControl
.
However, for some reason, the changes which arrive at the BindableProperty
in GenericControl
(either the default values from GenericPage
or externally set) are not propagated to the ControlViewModel
.
I have created a Repro Project for this issue: github.com/mlxyz/Xamarin-Forms-Binding-Repro
In the project, pressing the button will change the value of the bindable property in GenericPage
. However, the property of the ControlViewModel
is not updated for some reason (the top label).
Why is this the case and how can I fix it?
Thanks!
Note: I have also posted this question to stackoverflow: stackoverflow.com/q/62913492/10289094
However, the property of the ControlViewModel is not updated for some reason (the top label).
I've tested the code and reproduced the issue. The issue is caused by the binding for the custom control page.
The value of Test property is obtained from the page and set binding for the custom control.
<controls:Control Test="{Binding Source={x:Reference Root}, Path=BindingContext.Test, Mode=OneWay}" />
But the following code will set the view's BindingContext
to the viewModel class, it's not the same as the page's binding. This is why the top label is always the default when clicking the button. Try to remove the code from the view page.
<controls:GenericControl ...> <controls:GenericControl.BindingContext> <viewModels:ControlViewModel /> </controls:GenericControl.BindingContext> <ContentView.Content> ... </ContentView.Content> </controls:GenericControl>
I would like to have a separate view model for my Control class however, as I have to do some calculations there based on the properties that are passed in from the Page.
If you want to set separate viewModel for the control, just set data binding as usual. To update the UI of the contentView in the page, you could use MessagingCenter.
Answers
I've tested the code and reproduced the issue. The issue is caused by the binding for the custom control page.
The value of Test property is obtained from the page and set binding for the custom control.
But the following code will set the view's
BindingContext
to the viewModel class, it's not the same as the page's binding. This is why the top label is always the default when clicking the button. Try to remove the code from the view page.@YelinZh
Thank you for your answer!
I can indeed confirm, that the text is updated correctly, when I remove the
ControlViewModel
from theControl.xaml
page.I would like to have a separate view model for my
Control
class however, as I have to do some calculations there based on the properties that are passed in from thePage
.Is that possible?
Note: I can make it work with the separate view model by adding the following line in the
propertyChanged
handler inGenericControl
:(control.BindingContext as ControlViewModel).ControlViewModelTest = (Vector3)newvalue;
But I had thought that this would be automatically done by the binding in
Control
.If you want to set separate viewModel for the control, just set data binding as usual. To update the UI of the contentView in the page, you could use MessagingCenter.
@YelinZh
Sorry for the late reply.
I did some more research and the problem in my original question appears to be caused by the fact that one bindable property can not be bound to two viewmodel properties at the same time. This means that I cant use bindable properties in the control to relay data from the viewmodel of the page to the viewmodel of the control.
I don't really want to use the
MessagingCenter
for this use case as there arise a couple of other problems (e.g. threading problems as messages are sent from a separate thread initiated by ArKit and potential performance issues as there potentially will be 10s of messages sent per second), but I guess I have not other choice here.I will accept your post as the answer but I'm still searching for a better way to solve my issue.