Xamarin Forms

Source code for the completed version of this sample is available on GitHub here: https://github.com/billreiss/xamlnative/tree/master/XamarinForms/ListViewNoBinding

Note: This post was inspired by Jason Smith’s Evolve session discussing performance in Xamarin Forms. Jason gives a lot of great information and I highly recommend watching the video. At one point he mentions reducing the amount of data binding, and I was asked recently how you would do that in a ListView, so here is my solution. I am not a fan of over optimizing, so I would only use this if you are seeing performance issues in your ListView (scrolling hiccups etc) along with the other techniques Jason discusses in the video.

Data binding is great. It is convenient, promotes good practices of separation of concerns, reduces glue code, and many other things. It’s also not free. It’s actually pretty expensive since it relies on Reflection, and has memory and CPU performance implications. Hopefully someday Xamarin Forms will support compiled bindings like the Universal Windows Platform. Compiled bindings remove a lot of these issues, if you are doing native UWP development, definitely give them a look.

These negative impacts can multiply when displaying items in a ListView. There is a lot more data binding going on, since it is once per row. There are also row reuse strategies that may lead to more data binding while the user scrolls, which is when you want the best performance.

There are clear trade-offs here, but let’s assume you want the optimum performance even if it’s a bit more code intensive. How would you even do this inside a ListView item template? One way is to embed a ContentView and set its display properties in code.

Let’s say we have an app displaying a list of ItemViewModel as a ListView. Here is the ItemViewModel:

The XAML for displaying the ListView (using bindings) looks like this:

And the code behind to populate the ListView:

To populate some sample code, I’m just taking a lorem ipsum string and assigning a word to each row, restarting when the words run out. This is what the app looks like when running:

image

Now data binding two properties may not be a major performance hit, but consider more complex item templates. So how could we accomplish the same results without data binding? Where would the code go to pump the data into the item view cell? One way would be with a custom renderer, but then you would need to implement this on each platform. Another way is to create a custom ViewCell control, that is what we will do here.

First let’s refactor what we currently have, still using data binding, but moving the XAML for the ViewCell to its own class. In the Portable project, I can add a new Forms Xaml Page and call it ItemCell.

image

This generates a XAML file for layout and a code behind file. In ItemCell.xaml, we can modify it as follows, copying in the XAML from the item template and changing the outer type to ViewCell:

And for the ItemCell.xaml.cs code behind we need to tell it to inherit from ViewCell so that it matches the XAML file :

Finally we can change the MainPage to use this custom ViewCell:

Running this again, we get the same result as before, but now we have somewhere to put some code. Let’s see how we can remove the bindings in the ItemCell and update the view manually. In ItemCell.xaml, remove the bindings from the Label.Text properties:

Now the other thing we need to do is update the text when the data changes. Even though we aren’t using data binding ourselves, the BindingContext of the ViewCell is set to an item in the list. Since this ListView recycles cells, a limited number of ItemCell objects are created and they are reused, each time the BindingContext is set to the current row that the cell is being used for. So to avoid data binding inside the cell, and to set the text ourselves, we can override the BindingContextChanged method on the ItemCell, and inside there set the text.

Now if you run the app again, it should behave just as before, but we now have full control over when the data is refreshed and by doing it manually we are saving on Reflection costs. Note this assumes the data is read only, we are not listening for property changed events on the Id and Text properties of the ViewModel. You could add this by wiring and unwiring the PropertyChanged event in the OnBindingContextChanged method. I will cover this in the next post.

One last thing is it can be interesting to set breakpoints or add debug statements to the constructor and OnBindingContextChanged of the ItemCell. It can give you some insight into how row caching works in a Xamarin Forms ListView.

Leave a Reply

Your email address will not be published. Required fields are marked *