Skip to content

Instantly share code, notes, and snippets.

@anaisbetts
Created December 15, 2014 10:49
Show Gist options
  • Save anaisbetts/013d9fc97d8359373cf0 to your computer and use it in GitHub Desktop.
Save anaisbetts/013d9fc97d8359373cf0 to your computer and use it in GitHub Desktop.
Tabbed-view compatible RoutedViewHost
using System;
using Xamarin.Forms;
using Splat;
using ReactiveUI;
using System.Reactive.Linq;
using System.Reactive.Threading.Tasks;
using System.Diagnostics;
namespace ReactiveUI.XamForms
{
public class TabbedRoutedViewHost : NavigationPage, IActivatable
{
public static readonly BindableProperty RouterProperty = BindableProperty.Create<RoutedViewHost, RoutingState>(
x => x.Router, null, BindingMode.OneWay);
public RoutingState Router {
get { return (RoutingState)GetValue(RouterProperty); }
set { SetValue(RouterProperty, value); }
}
public RoutedViewHost() : this(null) {}
public RoutedViewHost(Router router = null)
{
this.WhenActivated(new Action<Action<IDisposable>>(d => {
bool currentlyPopping = false;
d(this.WhenAnyObservable(x => x.Router.Navigate)
.SelectMany(_ => pageForViewModel(Router.GetCurrentViewModel()))
.SelectMany(x => this.PushAsync(x).ToObservable())
.Subscribe());
d(this.WhenAnyObservable(x => x.Router.NavigateAndReset)
.SelectMany(_ => pageForViewModel(Router.GetCurrentViewModel()))
.SelectMany(async x => {
currentlyPopping = true;
await this.PopToRootAsync();
await this.PushAsync(x);
currentlyPopping = false;
return x;
})
.Subscribe());
d(this.WhenAnyObservable(x => x.Router.NavigateBack)
.SelectMany(async x => {
currentlyPopping = true;
await this.PopAsync();
currentlyPopping = false;
return x;
})
.Do(_ => ((IViewFor)this.CurrentPage).ViewModel = Router.GetCurrentViewModel())
.Subscribe());
var poppingEvent = Observable.FromEventPattern<NavigationEventArgs>(x => this.Popped += x, x => this.Popped -= x);
// NB: Catch when the user hit back as opposed to the application
// requesting Back via NavigateBack
d(poppingEvent
.Where(_ => !currentlyPopping && Router != null)
.Subscribe(_ => {
Router.NavigationStack.RemoveAt(Router.NavigationStack.Count - 1);
((IViewFor)this.CurrentPage).ViewModel = Router.GetCurrentViewModel();
}));
}));
Router = Router ?? Locator.Current.GetService<IScreen>().Router;
if (Router == null) throw new Exception("You *must* register an IScreen class representing your App's main Screen");
this.WhenAnyValue(x => x.Router)
.SelectMany(router => {
return router.NavigationStack.ToObservable()
.Select(x => (Page)ViewLocator.Current.ResolveView(x))
.SelectMany(x => this.PushAsync(x).ToObservable())
.Finally(() => {
var vm = router.GetCurrentViewModel();
if (vm == null) return;
((IViewFor)this.CurrentPage).ViewModel = vm;
this.CurrentPage.Title = vm.UrlPathSegment;
});
})
.Subscribe();
}
IObservable<Page> pageForViewModel(IRoutableViewModel vm)
{
if (vm == null) return Observable.Empty<Page>();
var ret = ViewLocator.Current.ResolveView(vm);
if (ret == null) {
var msg = String.Format(
"Couldn't find a View for ViewModel. You probably need to register an IViewFor<{0}>",
vm.GetType().Name);
return Observable.Throw<Page>(new Exception(msg));
}
ret.ViewModel = vm;
var pg = (Page)ret;
pg.Title = vm.UrlPathSegment;
return Observable.Return(pg);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment