Databinding Scope in Xamarin.Forms

A question I have seen coming by a lot is: how to trigger a command from the ContextAction on an item in the ListView. While it seems straight-forward, implementation can be a bit tricky. This has everything to do with the databinding scope. This post will give you some background and show you how it’s done.

If you’re more interested in a video walkthrough; I’ve got you covered. The same will be explained here in this post. For more content, please feel free to subscribe to my YouTube channel and/or Twitch.

Xamarin.Forms ListView/CollectionView data-binding explained!

Outlining the Case

I think the most obvious example case is the one I started this post with. In your app there is a ListView with items. As context actions on those items you have a delete action, but the delete Command doesn’t trigger. A screenshot of this scenario implemented on iOS can be seen underneath.

Screenshot of sample app with a ListView and a context action
Sample app with ListView and context action open on iOS

The Problem with Databinding Scope

While I say “problem”, it is not really a problem, it’s just the way things work. However, I can see why it is a bit confusing. What happens is that your page view model is bound to the complete page, which probably has a collection of items that is shown in the ListView. However, whenever we look at a single item in the ListView, the scope suddenly shifts to the object bound to that one cell. It can be hard to explain in words, I’ve tried to capture it in an image which you see below. It will probably become more clearer when we see some code.

Schematic overview of the scopes in screenshot
Same screenshot as above with schematic overview of scopes

Solution

So how do we go about this? Let me tell you the code for this is to be found here: https://github.com/jfversluis/DataBindingScopeSample.

First let’s have a look at my backing code. The first class ListModel is the class that represents one item in our collection. That means one item in our ListView. The MainPageViewModel class is the view model that is behind our whole page. In this class we have our collection that the ListView will show. Also notice it has a command to delete an item from this list.

https://gist.github.com/jfversluis/12d166a400f18688e523430258d70a15

Now, let’s have a look at the ListView I have in place. You can see the ItemsSource of the ListView is pointing to the MyItems property in MainPageViewModel.

https://gist.github.com/jfversluis/810462d21a525b8e9dfe81f6f225e8fe

You can already see where I’m going with this because of the comments. Because we point our ListView to the MyItems, that will mean each cell will be an instance of the ListModel object. That also means, that if we implement a context action as you can see in the first comment block, the {Binding DeleteCommand} will start looking for the DeleteCommand in the ListModel item. But we want our context action to look in the MainPageViewModel for this command.

It does not make any sense to add a delete command in each item. An item will not know if it’s part of a collection, let alone what code it needs to trigger to be deleted. We really want the delete command in the page view model and call upon that.

Point the Databinding Scope in the Right Direction

To make our databinding scope work correctly, what we need to do is just point the binding in the right direction. Notice how I added the x:Name="ListOfPeople" on the ListView. Now look at the uncommented block of the MenuItem. You will notice how the binding tag will now specify a path, pointing to the BindingContext.DeleteCommand. Before I will explain this, notice how I now also specify the source. And that source is pointing to the name of the ListView.

By implementing the binding like this, we point back to the ListView and from there we reach into the BindingContext property first. Within that object currently assigned to the BindingContext, we go into the DeleteCommand. In our sample that means, we go to the BindingContext of the ListView, which is the MainPageViewModel and in the MainPageViewModel we point to the DeleteCommand.

Now read all that again three times and you might start to get it πŸ˜‰

Databinding Scope with RelativeSource

Since Xamarin.Forms 4.3 there is also the RelativeSource binding which will make this a bit easier. You can see that in the third comment block in the code above.

I don’t want to overload you with more information right now, but be sure to check it out on the docs or read this great blog by my good friend Javier.

In Closing

I hope this post will make a bit clear how the scoping works. It’s kind of tough to explain, you probably need to play with it for a little to fully click. This example uses a ListView, but this also applies to CollectionView or CarouselView or any other control with a similar concept. Just try to analyze if you might be looking at an instance of one item as opposed to your page’s view model.

If you want to read a little bit more and see some real-life cases, check these StackOverflow answers. Here is one, two and three.

Once again, the code can be found on my GitHub: https://github.com/jfversluis/DataBindingScopeSample. If there are any questions or concerns, please let me know!

3 thoughts on “Databinding Scope in Xamarin.Forms”

  1. Thanks for this, I spent a couple of hours yesterday to use the new relative bindings and it didn’t appear to work. I then realised my use-case was ‘special’ as I was using data binding for Map arguments and that’s performed only once at construction, so no matter what binding I tried the values would never change.

    If you take a look here https://docs.microsoft.com/en-us/xamarin/xamarin-forms/user-interface/map/pins#display-a-pin then it illustrates the one use-case bindings will not work within the

    • Hm that sounds strange. I hope you got it working! If you think this is something that should be working, please feel free to open an issue on the Xamarin.Forms repository so we can discuss there πŸ™‚

      Thank you for reading!

  2. Hi Gerald, it was actually a misunderstanding on my part of what the ‘arguments’ were in the XAML, they are constructor arguments that only get called/set once. This makes bindings useless in that map argument use-case.

    In the end I worked around it, by waiting for the page is displaying lifecycle method, then get the location I want from my binding (MVVM model) and then invoke a method to update the map bounds directly from the page code-behind.

    It’s a shame they don’t offer a property to set this instead of a constructor argument.

Comments are closed.