Pared down version of Assisticant that can be used with Xamarin Starter Edition.
Data bind iOS and Android views to view models using dependency tracking. Create ViewControllers and Activities just like you are used to, write simple view model classes, and set up two-way data binding.
PM> Install-Package Assisticant.Starter
Declare all of your model fields using the Observable<T>
class. Initialize the field, optionally initialize the value
by passing it into the constructor. Write properties to access the observable fields.
using Assisticant.Fields;
public class MyModel
{
private Observable<string> _firstName = new Observable<string>("John");
private Observable<string> _lastName = new Observable<string>("Doe");
private Observable<string> _description = new Observable<string>();
public string FirstName
{
get { return _firstName.Value; }
set { _firstName.Value = value; }
}
public string LastName
{
get { return _lastName.Value; }
set { _lastName.Value = value; }
}
public string Description
{
get { return _description.Value; }
set { _description.Value = value; }
}
}
Write a class that holds a read only reference to the model. Initialize this reference in the constructor. Create properties that get and set properties of the model. You can do any kind of calculation that you want. This class has no base class.
public class MyViewModel
{
private readonly MyModel _model;
public MyViewModel(MyModel model)
{
_model = model;
}
public string Name
{
get { return _model.FirstName + " " + _model.LastName; }
}
public string Description
{
get { return _model.Description; }
set { _model.Description = value; }
}
}
In iOS, create a ViewController
. Create a private field of type BindingManager
. Also create a field to hold your view model.
using Assisticant.Binding;
public class MyViewController : ViewController
{
private BindingManager _bindings = new BindingManager();
private MyViewModel _viewModel = new MyViewModel(new MyModel());
}
In Android, create an Activity
. Create a private field of type BindingManager
. Also create a field to hold your view model.
using Assisticant.Binding;
public class MyActivity : Activity
{
private BindingManager _bindings = new BindingManager();
private MyViewModel _viewModel = new MyViewModel(new MyModel());
}
In iOS, call the BindingManager's Initialize method in ViewDidLoad
. Pass in the ViewController (this).
protected override void ViewDidLoad()
{
base.ViewDidLoad();
_bindings.Initialize(this);
}
In Android, call the BindingManager's Initialize method in OnCreate
. Pass in the Activity (this).
protected override void OnCreate()
{
base.OnCreate();
_bindings.Initialize(this);
}
In iOS, call the BindingManager's Bind methods in ViewWillAppear
. Pass in the UI control to bind, a lambda expression
for output, and optionally a second lambda expression for input.
protected override void ViewWillAppear()
{
base.ViewWillAppear();
// One way data bind
_bindings.BindText(nameLabel,
() => _viewModel.Name);
// Two way data bind
_bindings.BindText(descriptionEdit,
() => _viewModel.Description,
s => _viewModel.Description = s);
}
In Android, call the BindingManager's Bind methods in OnCreate, after the call to Initialize. Find the view to bind by ID, and pass it into bind. Also pass in a lambda expression for output, and optionally a second lambda expression for input.
protected override void OnCreate()
{
base.OnCreate();
_bindings.Initialize(this);
// One way data bind
_bindings.BindText(FindViewById<TextView>(Resources.Id.NameLabel),
() => _viewModel.Name);
// Two way data bind
_bindings.BindText(FindViewById<EditText>(Resources.Id.DescriptionEdit),
() => _viewModel.Description,
s => _viewModel.Description = s);
}
In iOS, call the BindingManager's Unbind method in ViewDidDisappear
.
protected override void ViewDidDisappear()
{
_bindings.Unbind();
base.ViewDidDisappear();
}
In Android, call the BindingManager's Unbind method in OnDestroy
.
protected override void OnDestroy()
{
_bindings.Unbind();
base.OnDestroy();
}
In your model, define lists using the ObservableList<T>
class. This has the same contract
as a List<T>
, but it participates in data binding. Access the list through methods and
properties.
public class AddressBook
{
private ObservableList<Person> _people = new ObservableList<Person>();
public IEnumerable<Person> People
{
get { return _people; }
}
public Person NewPerson()
{
var person = new Person();
_people.Add(person);
return person;
}
}
You could return a collection of model objects from the view model. But if you do, then you will not have an opportunity to add view-specific properties to those objects. So I recommend returning child view models instead.
Use Linq to project model objects into new view model objects.
public class AddressBookViewModel
{
private readonly AddressBook _addressBook;
public AddressBookViewModel(AddressBook addressBook)
{
_addressBook = addressBook;
}
public IEnumerable<PersonViewModel> People
{
get
{
return
from person in _addressBook.People
select new PersonViewModel(person);
}
}
}
When you define child view models, it's a good idea to define Equals
and GetHashCode
. This
helps Assisticant keep the items in the view consistent with the child objects.
public class PersonViewModel
{
private readonly Person _person;
public PersonViewModel(Person person)
{
_person = person;
}
public string Name
{
get { return _person.LastName + ", " + _person.FirstName; }
}
public override bool Equals(object obj)
{
if (obj == null || GetType() != obj.GetType())
{
return false;
}
return _person == ((PersonViewModel)obj)._person;
}
public override int GetHashCode()
{
return _person.GetHashCode();
}
}
To bind child view models to an iOS UITableView
or an Android ListView
, use the BindItems
method.
In iOS, pass in a function that binds each child to a UITableViewCell
.
_bindings.BindItems(listPeople,
() => _viewModel.People,
(cell, person, bindings) =>
{
bindings.BindText(cell.TextLabel,
() => person.Name);
});
In Android, pass in the identifier of the child layout, and a function that binds each child.
_bindings.BindItems(FindViewById<ListView>(Resource.Id.listPeople),
() => _viewModel.People,
Resource.Layout.Name,
(view, person, bindings) =>
{
bindings.BindText(view.FindViewById<TextView>(Resource.Id.textName),
() => person.Name);
});
The child bindings will be cleaned up when the item is removed from the list, or when you call Unbind on the parent binding. There is no additional work for you to do.