Skip to content

Instantly share code, notes, and snippets.

@kentcb
Created July 14, 2016 00:48
Show Gist options
  • Save kentcb/d8f310a4db40cc6833c5df48fa2de31a to your computer and use it in GitHub Desktop.
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
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