Advanced ListView Bindings

The traditional Binding

When first making contact with ListViews in an Xamarin Forms MVVM design you usually publish an ObservableCollection as a property of the ViewModel of your Page, bind the ItemSource of the ListView to it and then bind to the items in´the collection in your Datatemplate. Like this:

    [ImplementPropertyChanged]  //I use Fody here to not have to bother with INotifyPropertyChanged
    public class DemoListViewPageModel
    {
        public ObservableCollection<Dog> DogList

        public DemoListViewPageModel()
        {
            DogList = new ObservableCollection<Dog>();

            DogList.Add(new Dog() { Name = "Rex", Race = "German Sheppard" });
            DogList.Add(new Dog() { Name = "Barney", Race = "Poodle" });
            DogList.Add(new Dog() { Name = "Jimmy", Race = "Beagle" });
            DogList.Add(new Dog() { Name = "Rob", Race = "Labrador" });
        }
    }

    // Should be something that makes a bit of sense, so why not dogs
    [ImplementPropertyChanged]
    public class Dogs
    {
        public string Name {get; set}

        public string Race {get; set;}
    }

And in the Page then

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="Examples.DemoListViewPage">
  <ContentPage.Content>
    <StackLayout HorizontalOptions="CenterAndExpand" VerticalOptions="Center">
      <ListView ItemsSource="{Binding DogList}" HasUnevenRows="True">
        <ListView.ItemTemplate>
          <DataTemplate>         
            <ViewCell>
              <StackLayout HorizontalOptions="Start" >
                <Label x:Name="Name"  Text="{Binding Name}"  TextColor="Navy" FontSize="20"/>
                <Label x:Name="Race" Text="{Binding Race}"/>
              </StackLayout>
            </ViewCell>           
          </DataTemplate>
        </ListView.ItemTemplate>
      </ListView>
    </StackLayout>
  </ContentPage.Content>
</ContentPage>

That’s fine but

  • It violates MVVM in exposing the model behind it because the DataTemplate binds to model objects
  • The moment we want to store additional data for each item or execute a command from an ViewCell it get’s difficult

While the first might seem be an academic reason it really couples the model tightly to the view which makes changes in the model layer propagate to the view.

Let me elaborate on the second drawback.

  • Imagine we add a check mark (let’s asume we have such a control) to the ViewCell. Now we have to keep track of it’s state somewhere. We could bind its Toggle command to a Command in our ViewModel and also add a List<bool> checkedItems where we store the state for each item in the ListView.
  • We want to add a Add to Shopping Cart Button to our ViewCell. Again we could bind all Buttons of the ListView to a Command in our ViewModel that calls a WebService to add the item to the shopping cart. (Ok this example might be somewhat contrived)

Both both enhancements need additional code in the ViewModel to figure out which item the Command should be exceuted on which should not be the job of the ViewModel. The real reason for the problem is that our ViewModel is the ViewModel for the whole Page. The ListView is a container that holds a list of ItemViews that are defined in the DataTemplateof the ListView. So the consequence should be: Give each ItemView its own ViewModel. Which means our page’s ViewModel no longer exposes a ObservableColletion<Dogs> but ObservableColletion<DogsItemViewModel>

Introducing ViewModels for each ListView item

public class DogsItemViewModel
{
    private Dog DogObject;
    ICommand AddToCartCommand;

    public DogsItemViewModel(Dog theDog)
    {
        DogObject = theDog;
        AddToCartCommand = new Xamarin.Forms.Command(AddToCart);
    }

    public string Name => DogObject.Name;
    public string Race => DogObject.Race.ToUpper();
    public bool IsChecked { get; set; }

    void AddToCart()
    {
        WebService.Instance.AddToCart(DogObject);  // Whatever this will do ;-) 
    }
}

Now the DogsItemViewModel can handle all interaction with the ItemView on it’s own. You can even do conversions on the model data without the need to write an ValueConverter like I did with the Race property.

Now we refactor our ViewCell and make it a custom class. From it’s code behind file you can now even access it’s elements using x:Name

<?xml version="1.0" encoding="UTF-8"?>
<ViewCell xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" 
             x:Class="YourNameSpace.DogItemView">


  <StackLayout HorizontalOptions="Start" >
    <CheckBox State = "{Binding IsChecked}" />
    <Label x:Name="Name"  Text="{Binding Name}"  TextColor="Navy" FontSize="20"/>
    <Label x:Name="Race" Text="{Binding Race}"/>
    <Button Text="Add to Cart" Command="{Binding AddToCartCommand}"/>
  </StackLayout>
</ViewCell>

and our Page like this:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="Examples.DemoListViewPage">
  <ContentPage.Content>
    <StackLayout HorizontalOptions="CenterAndExpand" VerticalOptions="Center">
      <ListView ItemsSource="{Binding DogList}" HasUnevenRows="True">
        <ListView.ItemTemplate>
          <DataTemplate>         
            <DogItemView BindingContext = "{Binding}" />
          </DataTemplate>
        </ListView.ItemTemplate>
      </ListView>
    </StackLayout>
  </ContentPage.Content>
</ContentPage>

Looks cool, doesn’t it?

Virtual ObservableCollection

Virtualised collection views like ListView or CarouselView are great as they generate their views on the fly based on a DataTemplate. The problem is they need a collection that contains all elements that you want to display. Sometimes this is not feasible because the collection would be too big or it would take too long to load it completely from a remote location.

After some research I found out that the ItemViews as minimum need  an object that implements IReadOnlyList<T>, INotifyCollectionChanged as Itemsource and even there they only use the [int index] and Count property. So I came up with this handy class:

using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Diagnostics;

namespace Helpers{
    public abstract class VirtualObservableCollection<T> : IReadOnlyList<T>, INotifyCollectionChanged
    {
        public IEnumerator<T> GetEnumerator()
        {
            throw new System.NotImplementedException();
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }

        public int Count {
            get { return ItemCountRequest(); }
        }

        public T this[int index]
        {
            get { Debug.WriteLine("Index: {0}",index);
                 return OnDataRequest(index);
            }
        }

        public event NotifyCollectionChangedEventHandler CollectionChanged;


        public void SignalCollectionChanged()
        {
            CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
        }

        protected abstract  T OnDataRequest(int index);
        protected abstract int ItemCountRequest();


    }
}

You only have to derive your own class from it and override OnDataRequest and ItemCountRequest. OnDataRequest has to return the item that should be displayed for a given index. ItemCountRequest must return the maximum number of items that will be displayed.

 

How to Distribute Files with your App

Often we need to distribute some files together with our App like a prepopulated database, settings files, image etc. To do this there are basically three methods:

Embedded Resoures

  • Quite good for Images like icons
  • read only
  • not directly accessable for file IO
  • Can be accessed from PCL Files
  • App has to be rebuilt

https://developer.xamarin.com/guides/xamarin-forms/working-with/files/#Loading_Files_Embedded_as_Resources

As Android Assets / iOS bundled Resource

  • read only, so you have to copy them somewhere else if you want to have  write access
  • not accessible from PCL without Dependency Service
  • App has to be rebuilt
  • Has to be added to each platform project differently

 

Android
  • read only
  • not directly accessable for file

Example of reading a JSON File:

using (var reader = new StreamReader(assets.Open("SampleData.json")))
{
   var json = new JsonSerializer();
   list = (List)json.Deserialize(reader, typeof(List));
}

 

iOS
  • read only
  • directly accessible for file
using (var reader = new StreamReader(File.Open("./Samples/SampleData.json", FileMode.Open,FileAccess.Read)))
{
   var json = new JsonSerializer();
   list = (List)json.Deserialize(reader, typeof(List));
}
return list;
 The path corresponds to the Folders below the Resoures folder of your iOS project.

Download from server at first startup

IMHO the most flexible solution.

  • App does not have to be rebuilt
  • writeable
  • directly accessable for file IO
  • can also be used to update an installed App

Often people shy way from this as it seems a lot of effort especially if you have a lot of files.

Luckily I happen to have published just yesterday a class calles FileStorage which offers a needful function called PopulateDataFolderFromZipUri

public async Task PopulateDataFolderFromZipUri(string uri, 
                                               string outFolder)
which will download a ZIP file from uri and extract it to outFolder which path is relative to your app’s data directory

 

What you always wanted to know about Gorilla Player….

One of the most requested features of Xamarin Forms is a visual Xaml Editor. Sure, it would be nice, but it would never show you how your App will look on different devices. So I think the way that the developers of Gorilla Player choose is the right one. It’s not too difficult to write a Xaml Page. After some time you do a good piece by copy and modify existing code or create your own custom views for reuse.  But it’s annoying to compile and deploy your App to see even minor design changes. Here comes Gorilla player into play.

Brand logo

So what is it? Gorilla Player let’s you preview your Xaml Files in real time on multiple devices or Emulators in parallel. Any change to your Xaml files are reflected immediately on all devices.

To make this work you have to install a Player App which is available in the App Stores and the Gorilla Server App on your PC. After starting the Server App you can connect as many devices as you want to the Server by starting the Player App. If you then open a Xaml File in VS or XS it will be automatically displayed on the devices.

Tip:
If the page is not displayed try opening another file in your IDE and then switch back to the Xaml file. If this don’t help go to the Tools->Gorilla menu and disconnect / connect your IDE.  Sometimes your Player App will not connect if you click on the discovered server. In this case switch to manual to connect.

This is how it looks like previewing  the same page on iOS on the VS iOS emulator in parallel to MS Android emulator.

monkey

This sounds pretty easy, but there are some pitfalls on your way:

  • If you use custom renderers and markup extensions and resources  will not be displayed.
  • Any bindings will not work 🙁

The solution for this is using the Gorilla SDK this actually allows you to include the Gorilla client into your App and therefore access all your assemblies including renderers, markup extensions, custom page base classes and resources. Sound great, BUT getting this set up is not as easy as it sounds:

  • Biggest problem, the documentation on the Gorilla Website is not up to date. There is a more complete one on their github page
  • Registering your assemblies can be tricky

I won’t repeat the content of their SDK documentation on github here. Instead I will add some things that I found important while working with gorilla:

  • Make sure that you add all assemblies that contain renderes etc. to the gorilla.json file AND register it in your App startup code using RegisterAssembyByType with types from all assemblies that you want to use.
  • Important include also the main PCL assembly of your App
  • Gorilla recommend adding the SDK to your App project. I personally prefer to have a separate GorillaDesign solution which loads and deploys fast to your device because you will have to deploy it every time you want to add a new assembly.  When I’m satisfied with the look I will copy the Xaml back to my App project.
  • You can provide Sample Data to your databindings using a json file that is also updated in real time. When I tried this I had problems to get this working together with the SDK. What I did is adding a static item source to my Xaml file. This way you can use all your Custom classes in bindings:

staticitemsource

Important: To make this work you have to include the name space containing your custom classes to the xaml file.

  • One general tip when tweaking Xaml design is to use the BackgroundColor that almost all visual Elements and Layouts have to see which dimensions these Elements really have like this here

gorilla

If you still have problems getting Gorilla to work, drop leave a comment or mention me on Slack and I will try to update this post with more information.

 

Hello world!

Hi,

after long thoughts I decided to start a blog too. My I have two goals with this blog.

  1. Share experiences that I made while developing, perhaps saving others time making not the same mistakes
  2. Write up information that I myself always forget while developing.

I hope you will enjoy reading.

Best

Thomas

P.S.: I’m not a native speaker so please have patience with my writing. I also will not spend a lot of time polishing the design  of my posts. I hope the content will make up for it.