Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Binding a property of a relative source root parent control to an attached property of a child class - crash the app

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

like image 474
Cfun Avatar asked Nov 18 '25 23:11

Cfun


2 Answers

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.

like image 134
Clay Brooks Avatar answered Nov 20 '25 11:11

Clay Brooks


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.

like image 24
Cfun Avatar answered Nov 20 '25 13:11

Cfun



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!