Xamarin.Forms parent BindingContext in DataTemplate in the XAML

ThomasClopThomasClop ✭✭USMember ✭✭
edited March 2018 in Xamarin.Forms

After few hours of research, I finally found a solution to my problem. I saw that other people had the same problem : I have a listview with a custom viewcell as datatemplate, the contentpage viewmodel has a shared command "RemoveItem" and I want to invoke it from my custom viewcell.
The original error was : "Xamarin.Forms.Xaml.XamlParseException: No Property of name ElementName found".

In the XAML of my contentpage ("cells" reference the namespace with my customs viewcells):

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Name="cart"/>
<DataTemplate>
    <cells:CartViewCell ParentContext="{Binding BindingContext, Source={x:Reference cart}}"/>
</DataTemplate>

In the class of my custom viewcell:

public partial class CartViewCell : ViewCell
    {
        public CartViewCell()
        {
            InitializeComponent();
        }

        public static readonly BindableProperty ParentContextProperty =
            BindableProperty.Create("ParentContext", typeof(object), typeof(CartViewCell), null, propertyChanged: OnParentContextPropertyChanged);

        public object ParentContext
        {
            get { return GetValue(ParentContextProperty); }
            set { SetValue(ParentContextProperty, value); }
        }

        private static void OnParentContextPropertyChanged(BindableObject bindable, object oldValue, object newValue)
        {
            if (newValue != oldValue && newValue != null)
            {
                (bindable as CartViewCell).ParentContext = newValue;
            }
        }
    }

And in the XAML of my customviewcell:

<ViewCell xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Name="cartCell"><Image.GestureRecognizers>
    <TapGestureRecognizer Command="{Binding ParentContext.RemoveItem, Source={x:Reference cartCell}}" CommandParameter="{Binding .}"/>
</Image.GestureRecognizers>

Sorry if I made some mistakes, french guy :blush:
If you have a better solution I am interested because it's look like a hack for me.

Posts

  • MulflarMulflar ✭✭ ESUniversity ✭✭

    OMG!! THANKS ;-)

  • Samuel.4254Samuel.4254 USMember

    OH FINALLY A SOLUTION THAT WORKS !
    Thanks so much ! I was stuck 2 days on this !

  • SzeYuanNgSzeYuanNg MYMember

    Thank you so much. your solution is work.
    finally I can go sleep.

  • NMackayNMackay mod GBInsider, University mod

    @ThomasClop

    Love posts like this, it will save people hours of head banging.

  • TomtomFooBarTomtomFooBar ✭✭ GTMember ✭✭
    edited June 2018

    Nice solution! But I was wondering, isn't the assignment in your OnParentContextPropertyChanged redundant?
    I mean, the propertyChanged handler of your BindableProperty is invoked after the property is assigned the new value, so why assign it again in the handler?

  • 80cmdude80cmdude Member

    I find that for 99% of all instances the one by @TobyK works perfectly and is nice and easy. However i just had an instance where i was forced to use the one from @ThomasClop. The instance was when I had a bindable collection on a stack layout with a datatemplate and i needed the tap event of each cell to trigger a command on the parent view model. The only way to get this to work was with @ThomasClop suggestion :)

    Hope this helps

  • GraverobberGraverobber ✭✭✭ Member ✭✭✭
    edited June 20

    @TobyK
    This only works as long as the ViewCell layout is described in the same XAML file as the Source you're referencing in you Binding, right?

    If I define the View cell in a different file and do something like

    <ListView ItemsSource="{Binding Items}" SelectedItem="{Binding Source={x:Reference Picker}, Path=BindingContext.Selected}">
    <ListView.ItemTemplate>
        <DataTemplate>
                 <views:MyCell />
             </DataTemplate>
    </ListView.ItemTemplate>
    

    then I can not reference Picker in MyCell, correct? In my case it crashes because it can not be found.

  • TobyKTobyK ✭✭✭ GBMember ✭✭✭
    edited June 20

    @Graverobber The data context for view:MyCell should be inheriting, if not I would look at setting the data context for your MyCell, something along the lines of:-

    <ListView ItemsSource="{Binding Items}" SelectedItem="{Binding Source={x:Reference Picker}, Path=BindingContext.Selected}">
    <ListView.ItemTemplate>
        <DataTemplate>
                 <views:MyCell DataContext={Binding YourBindingContext} />
             </DataTemplate>
    </ListView.ItemTemplate>
    

    Perhaps using the {x:Reference name} method - and x:Name'ing xaml elements.

    or even simpler:

    <ListView ItemsSource="{Binding Items}" SelectedItem="{Binding Source={x:Reference Picker}, Path=BindingContext.Selected}">
    <ListView.ItemTemplate>
        <DataTemplate>
                 <views:MyCell DataContext={Binding .} />
             </DataTemplate>
    </ListView.ItemTemplate>
    
  • GraverobberGraverobber ✭✭✭ Member ✭✭✭

    @TobyK
    Thanks for your reply.
    There is no DataContext parameter for ViewCells. Or is DataContext equivalent to ThomasClops ParentContext Bindable property?
    Or did you mean BindingContext? In that case it would change the BindingContext of the whole ViewCell, meanwhile what I try to achieve is to forward/send back the tap command from the view cell to the content pages ViewModel while maintaining the normal information like the persons name, phone, photo etc from the actual ViewCell ViewModel.

  • TobyKTobyK ✭✭✭ GBMember ✭✭✭
    edited June 20

    @Graverobber sorry been doing wpf work.. Have you tried wrapping your views:MyCell in ViewCell tags? I thought it was a ViewCell object.

    <ListView ItemsSource="{Binding Items}" SelectedItem="{Binding Source={x:Reference Picker}, Path=BindingContext.Selected}">
    <ListView.ItemTemplate>
        <DataTemplate>
            <ViewCell>
                     <views:MyCell BindingContext={Binding .} />
            </ViewCell>
         </DataTemplate>
    </ListView.ItemTemplate>
    

    I am assuming your Items contains name, photo, etc.

  • GraverobberGraverobber ✭✭✭ Member ✭✭✭

    @TonyK
    Thanks again for your reply :)

    I just realised that I didn't fully paid attention to your original solution, I missed that you wrapped the StackLayout into a DataTemplate too. Perhaps that's the difference.

  • TonyTruppTonyTrupp ✭✭ USUniversity ✭✭
    edited June 27

    This solution worked for me, also prefixing the "BindingContext." after referencing the parent page.

    Command="{Binding BindingContext.CommandFromParent, Source={x:Reference myPageName} }"

Sign In or Register to comment.