In Part 1 I discussed that I had settled on using the TabbedPage for navigation within my application. Like the MasterDetailPage, the TabbedPage lacks the ability to bind to the properties of its viewmodel to follow the MVVM pattern out of the box. I was, though, able to find an example of a BindablePicker that gave me all the tooling I needed to create my own.
View
<?xml version="1.0" encoding="utf-8" ?> <TabbedPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="MyProject.RootPage" Children="{Binding Tabs}" SelectedItem="{Binding CurrentTab, Mode=TwoWay}"> </TabbedPage>
CodeBehind
public partial class RootPage : TabbedPage { public static readonly BindableProperty ChildrenProperty = BindableProperty.Create<RootPage, IEnumerable>(x => x.Children, null, BindingMode.TwoWay, propertyChanged: OnItemsSourcePropertyChanged); public RootPage() { InitializeComponent(); } public new IEnumerable<Page> Children { get { return base.Children; } set { SetValue(ChildrenProperty, value); } } private static void OnItemsSourcePropertyChanged(BindableObject bindable, IEnumerable value, IEnumerable newValue) { var tabbedPage = (TabbedPage)bindable; var notifyCollection = newValue as INotifyCollectionChanged; if (notifyCollection != null) { notifyCollection.CollectionChanged += (sender, args) => { if (args.NewItems != null) { foreach (var newItem in args.NewItems) { tabbedPage.Children.Add((Page)newItem); } } if (args.OldItems != null) { foreach (var oldItem in args.OldItems) { tabbedPage.Children.Remove((Page)oldItem); } } }; } if (newValue == null) return; tabbedPage.Children.Clear(); foreach (var item in newValue) tabbedPage.Children.Add((Page)item); } }
ViewModel
public class RootPageViewController : ViewModelBase { INavigableTabBuilder tabBuilder; Page currentTab; ObservableCollection<Page> tabs; public RootPageViewController(INavigableTabBuilder tabBuilder) { this.tabBuilder = tabBuilder; } public ObservableCollection<Page> Tabs { get { return tabs ?? (tabs = new ObservableCollection<Page> { tabBuilder.CreateFirstTab(), tabBuilder.CreateSecondTab(), }); } private set { SetProperty(ref tabs, value); } } public Page CurrentTab { get { return currentTab ?? (currentTab = Tabs[0]); } set { SetProperty(ref currentTab, value); } } }
Because of the two-way binding, I can now set which tab is currently selected by setting
CurrentTab = Tabs[1];
in the ViewModel’s constructor if I like… and write the appropriate tests to ensure that whatever tab I want selected has been selected.
Here we go…
[…] Part 2, I created a pattern where I could use the MVVM pattern to manage the navigation on a TabbedPage. […]