Created
July 14, 2016 00:48
-
-
Save kentcb/d8f310a4db40cc6833c5df48fa2de31a to your computer and use it in GitHub Desktop.
A base class for XF Reactive views in lieu of a fix for https://github.com/reactiveui/ReactiveUI/issues/1133
This file contains hidden or 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
namespace ReactiveUI.XamForms | |
{ | |
using System; | |
using System.ComponentModel; | |
using System.Linq; | |
using System.Reactive; | |
using System.Reactive.Linq; | |
using Xamarin.Forms; | |
// The ReactiveContentView that comes with RxUI does not implement ICanActivate. As such, the activation-for-view-fetcher | |
// will currently attempt to detect the comings and goings of the view as best it can. Unfortunately, the current version | |
// of Xamarin Forms does not provide the requisite hooks to properly detect when a View comes and goes, so activation is | |
// currently broken for that scenario. | |
// | |
// If, instead, we use this implementation we can have activation work properly for views. The key is for the containing | |
// Page to assign itself to the Page property. And if the Page removes the View from its hierarchy, it should null out the | |
// Page property. | |
public class ReactiveContentViewBase<TViewModel> : ContentView, IViewFor<TViewModel>, ICanActivate | |
where TViewModel : class | |
{ | |
public static readonly BindableProperty ViewModelProperty = BindableProperty.Create( | |
nameof(ViewModel), | |
typeof(TViewModel), | |
typeof(ReactiveContentViewBase<TViewModel>), | |
defaultBindingMode: BindingMode.OneWay); | |
public static readonly BindableProperty PageProperty = BindableProperty.Create( | |
nameof(Page), | |
typeof(Page), | |
typeof(ReactiveContentViewBase<TViewModel>), | |
defaultBindingMode: BindingMode.OneWay); | |
public TViewModel ViewModel | |
{ | |
get { return (TViewModel)GetValue(ViewModelProperty); } | |
set { SetValue(ViewModelProperty, value); } | |
} | |
object IViewFor.ViewModel | |
{ | |
get { return ViewModel; } | |
set { ViewModel = (TViewModel)value; } | |
} | |
public Page Page | |
{ | |
get { return (Page)GetValue(PageProperty); } | |
set { SetValue(PageProperty, value); } | |
} | |
private IObservable<bool> IsActivated => | |
this | |
.WhenAnyValue(x => x.Page) | |
.Select( | |
page => | |
page == null ? | |
Observable.Return(false) : | |
GetIsActivatedForPage(page) | |
.CombineLatest( | |
GetIsVisibleForView(this), | |
(isPageActivated, isViewActivated) => isPageActivated && isViewActivated)) | |
.Switch(); | |
public IObservable<Unit> Activated => | |
this | |
.IsActivated | |
.Where(isActivated => isActivated) | |
.Select(_ => Unit.Default); | |
public IObservable<Unit> Deactivated => | |
this | |
.IsActivated | |
.Where(isActivated => !isActivated) | |
.Select(_ => Unit.Default); | |
private static IObservable<bool> GetIsVisibleForView(View view) | |
{ | |
var propertyChanged = Observable.FromEventPattern<PropertyChangedEventHandler, PropertyChangedEventArgs>( | |
x => view.PropertyChanged += x, | |
x => view.PropertyChanged -= x); | |
return propertyChanged | |
.Where(x => x.EventArgs.PropertyName == nameof(view.IsVisible)) | |
.Select(_ => view.IsVisible) | |
.StartWith(view.IsVisible); | |
} | |
private static IObservable<bool> GetIsActivatedForPage(Page page) => | |
Observable.Merge( | |
Observable.FromEventPattern<EventHandler, EventArgs>(x => page.Appearing += x, x => page.Appearing -= x).Select(_ => true), | |
Observable.FromEventPattern<EventHandler, EventArgs>(x => page.Disappearing += x, x => page.Disappearing -= x).Select(_ => false)); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment