6 Comments

showing the running sample app

This post will focus specifically on how to bind collection of data to a view. In another post I already focused on how to use bindings for data entry, be sure to read that one to get more insight into data binding under iOS with MVVM Light. In this post we will display a list of people using the standard UITableView iOS UI control. Via two buttons we will be able to add or remove a person. The people will be randomly generated by a service but first things first. Let’s start by creating the view.

Model

The people are generate with the help of the NameGenerator.cs, which is a class that generates random first- and last names.

// As found on https://digitaltoolfactory.net/blog/2012/04/how-to-make-a-random-name-generator-in-c/
public static class NameGenerator
{
    public static Random rnd = new Random();

    public static string GenRandomLastName()
    {
        List<string> lst = new List<string>();
        string str = string.Empty;
        lst.Add("Smith");
        // ...

        str = lst.OrderBy(xx => rnd.Next()).First();
        return str;
    }
    public static string GenRandomFirstName()
    {
        List<string> lst = new List<string>();
        string str = string.Empty;
        // male
        lst.Add("Aiden");
        // ...

        //female

        lst.Add("Sophia");
        // ...

        str = lst.OrderBy(xx => rnd.Next()).First();
        return str;
    }
}

This class will be accessed form the view models, but let’s first setup the view.

View

The view consists of a UITableView and two UIButtons which we add in the Storyboard and name them PeopleTableView, AddPersonButtonand  RemovePersonButton.

showing designer when creating the iOS view

After setting up the view next thing on the list is creating the view model.

View Model

In the view model we implement an ObservableCollection to hold the list of people and two RelayCommands which when invoked add and remove a person from the list:

public class MainViewModel : ViewModelBase
{
    public MainViewModel()
    {
        AddPersonCommand = new RelayCommand(AddPerson);
        RemovePersonCommand = new RelayCommand(RemovePerson);
    }

    public RelayCommand AddPersonCommand { get; set; }
    public RelayCommand RemovePersonCommand { get; set; }
    public ObservableCollection<Person> People { get; private set; }

    public async Task InitAsync()
    {
        if (People != null) return;

        People = new ObservableCollection<Person>();
        var people = await InitPeopleList();
        foreach (var person in people)
        {
            People.Add(person);
        }
    }
}

The list data is initialized asynchronously in an initialisation method. Now what is left to do is connect the view model with the view via bindings.

Configuring Bindings

The bindings are setup in the RootViewController.cs, which is attached to the View which we are about to populate and interact with. The command bindings are implemented in the ViewDidLoad method:

public override async void ViewDidLoad()
{
    base.ViewDidLoad();

    await Vm.InitAsync();

    // Setup bindings
    AddPersonButton.SetCommand("TouchUpInside", Vm.AddPersonCommand);
    RemovePersonButton.SetCommand("TouchUpInside", Vm.RemovePersonCommand);
}

The UITableView’s source is set in the ViewWillAppear method - if done in the ViewDidLoad method you will be initially showed nothing until you start scrolling the blank list.

public override void ViewWillAppear(bool animated)
{
    base.ViewWillAppear(animated);

    _tableViewController = Vm.People.GetController(CreatePersonCell, BindCellDelegate);
    _tableViewController.TableView = PeopleTableView;
}

In the CreatePersonCell method the cell we will display gets created.:

private UITableViewCell CreatePersonCell(NSString cellIdentifier)
{
    return new UITableViewCell(UITableViewCellStyle.Default, "Gnabber");
}

In case you are wondering: “But what about cell recycling?”. Well there is some good news, MVVM Light takes care of that for you. That being said currently (version 5.1.1) does not support multiple cell templates for one collection.

The BindCellDelegate sets the content of the cell:

private void BindCellDelegate(UITableViewCell cell, Person person, NSIndexPath path)
{
    cell.TextLabel.Text = person.FullName;
}

When all is setup we can start up the app you will be able to add and remove people to the list without having to react any further to the events or updating the table source any further.

Conclusion

In this post you saw how you can bind a list of data to an iOS UITableView. Further demonstration showed how easy it is to change the content of the list and updating the UI with the new data. MVVM Light not only allows to integrate your view models nicely in under iOS but only provides some nice little helpers when it comes down to creating an iOS TableSource.

Thanks again to Laurent Bugnion providing us with this great library.

You can find the whole sample on GitHub.

Comments

Comment by Yurkin

Hi.
Thanks for interesting articles.
I have a questions:
What about custom TableViewCells?
What about UICollectionViewController and MvvmLight? Cause there is no observable collection view controller in mvvm light?

Yurkin
Comment by Mark

Hi Yurkin.
Thank you for your kind words. Regarding your questions:
Yes you can use custom TableViewCells, as all cells (inlcuding custom) inherit from UITableViewCell, you will have to perform a cast in the BindCellDelegate.
Currently you would have to implement that on your own, but since MVVM Light is open source and the UICollectionVC is very similiar to the UITableViewVC it should not be that much of a hassle (did this for the Android RecyclerView). See the link bellow.

HTH

mvvmlight.codeplex.com/.../latest
Under: GalaSoft.MvvmLight.Platform (iOS), Helpers, ObservableTableViewController.cs

Comment by Yurkin

Hi Mark.
Thanks for clear answers.
I have one more question:
I am currently developing an app for two platforms: W10M and Android. What is the preferred approach for declaring platform specific services? Should I have multiple instances of viewmodellocator for each platform or one instance in shared code and declare platform specific services in App class for each platform?

Yurkin
Comment by Mark

Hi Yurkin
Good to hear they helped :)
I'm just in the writing of that blogpost currently. The short answer is I usually have multiple locators. In the PCL for all the shared code and in the platform projects for all the platform dependent services. Since the locator is just a class you can invoke the pcl locator from the platform which in turn is called by e.g. the app class for Win10.

Comment by Luke

Hi Mark, Great article, helped a lot
. I was wondering whats the best way to bind a selected item on the Table to a property on the view model?

Luke
Comment by Dheeraj

What if person.FullName changes? Will the UI update automatically? If not, what needs to be done to set up this binding?

Dheeraj
Post comment