I am trying to bind an attached property of an Entry control to a property of it root parent which is a ContentView:
(Mainly to use/access the ViewModel(BindingContext) of the called view in the NumBehaviors class)
The application crashes before even starting without clear spot of the problem in the debug:
Operation is not valid due to the current state of the object.
At Xamarin.Forms.Binding.ApplyRelativeSourceBinding (Xamarin.Forms.BindableObject targetObject, Xamarin.Forms.BindableProperty targetProperty) [0x00041] in D:\a\1\s\Xamarin.Forms.Core\ Binding.cs:153
[ERROR] FATAL UNHANDLED EXCEPTION: System.InvalidOperationException: Operation is not valid due to the current state of the object.
1- Am I doing something wrong (maybe with BindingContext)? Or it is Xamarin still not handling bindings as good as wpf (like the lack of ElementName) ?
2- Why the debug error/exceptions are not so explicit ?
MainView.xaml:
<ContentView xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:Helpers="clr-namespace:App.Helpers"
xmlns:Behaviors="clr-namespace:App.Behaviors"
HorizontalOptions="FillAndExpand"
VerticalOptions="Center"
x:Class="App.Views.MainView">
<Entry>
<Entry.Behaviors>
<Behaviors:NumBehaviors Helpers:HelperClass.Vm=
"{Binding Source={RelativeSource AncestorType={x:Type ContentView}}, Path=VM}"/>
</Entry.Behaviors>
</Entry>
HelperClass.cs
public static BindableProperty VmProperty =
BindableProperty.Create("Vm", typeof(object), typeof(HelperClass), null);
public static object GetVm(BindableObject bindable)
{
return (object)bindable.GetValue(VmProperty);
}
public static void SetVm(BindableObject bindable, object value)
{
bindable.SetValue(VmProperty, value);
}
MainView.cs
public object VM {get; set;}
public MainView()
{
InitializeComponent();
VM = (object) new MyViewModel();
BindingContext = VM;
}
NumBehaviors.cs
...
void OnEntryTextChanged(object sender, TextChangedEventArgs args) {
var usedvm = HelperClass.GetVm((BindableObject)sender);
}
I could maybe use the following but I think it is a bad idea, it is useless when the BindingContext of Entry is different from the one of it root parent also it does not look flexible:
...
void OnEntryTextChanged(object sender, TextChangedEventArgs args) {
var usedvm = ((Entry)sender).BindingContext;
}
PS: Maybe my approach is not good, I am open to better approaches to achieve that.
EDIT
Using Xamarin.Forms 4.8.0.1364
Also tried with AncestorType={x:Type local:MainView}} instead of AncestorType={x:Type ContentView}} gave the same result.
Maybe it is related to this opened issue of Xamarin.Forms [Bug] Compiled bindings not working when using AncestorType #9839
RelativeSource can find its ancestor's binding context but behaviors are not in the current visual tree.
I suggest using this to indicate the binding context:
Command="{Binding BindingContext.YourCommand, Source={x:Reference PageName}}"
YourCommand must be accessible from BindingContext and PageName must be set in the attributes of the enclosing ContentPage like <ContentPage ... x:Name="PageName"
More details can be found here. Props to the guy from that link who gave the initial solution.
Another solution to avoid this issue would be simply to change the design by moving the Attached property from <Entry.Behaviors:NumBehaviors> to <Entry>:
<Entry Helpers:HelperClass.Vm="{Binding Source={RelativeSource AncestorType={x:Type ContentView}},
Path=VM}">
<Entry.Behaviors>
<Behaviors:NumBehaviors/>
</Entry.Behaviors>
</Entry>
This move will also fix the mistake I made in my question code, I was attaching the property Vm to the object <Entry.Behaviors:NumBehaviors> while I was trying to get it from object Sender(=Entry) (var usedvm = HelperClass.GetVm((BindableObject)sender);) which would always returns null.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With