Created
December 15, 2014 10:49
-
-
Save anaisbetts/013d9fc97d8359373cf0 to your computer and use it in GitHub Desktop.
Tabbed-view compatible RoutedViewHost
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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