Skip to content
Browse files

Backport Activation to RxUI 4

  • Loading branch information...
1 parent 942ef55 commit ff1d1e7b56b941a65a76ab6270dc4492a3337425 Paul Betts committed Jan 30, 2014
View
32 ReactiveUI.Xaml/ActivationForViewFetcher.cs
@@ -0,0 +1,32 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reactive;
+using System.Reactive.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Reflection;
+
+#if WINRT
+using Windows.UI.Xaml;
+#endif
+
+namespace ReactiveUI.Xaml
+{
+ public class ActivationForViewFetcher : IActivationForViewFetcher
+ {
+ public int GetAffinityForView(Type view)
+ {
+ return (typeof(FrameworkElement).IsAssignableFrom(view)) ? 10 : 0;
+ }
+
+ public Tuple<IObservable<Unit>, IObservable<Unit>> GetActivationForView(IViewFor view)
+ {
+ var fe = view as FrameworkElement;
+ return Tuple.Create(
+ Observable.FromEventPattern<RoutedEventHandler, RoutedEventArgs>(x => fe.Loaded += x, x => fe.Loaded -= x).Select(_ => Unit.Default),
+ Observable.FromEventPattern<RoutedEventHandler, RoutedEventArgs>(x => fe.Unloaded += x, x => fe.Unloaded -= x).Select(_ => Unit.Default));
+ }
+ }
+}
View
1 ReactiveUI.Xaml/ReactiveUI.Xaml.csproj
@@ -130,6 +130,7 @@
<Reference Include="WindowsBase" />
</ItemGroup>
<ItemGroup>
+ <Compile Include="ActivationForViewFetcher.cs" />
<Compile Include="BindingTypeConverters.cs" />
<Compile Include="CommandBinding.cs" />
<Compile Include="CreatesCommandBinding.cs" />
View
3 ReactiveUI.Xaml/ReactiveUI.Xaml_Net45.csproj
@@ -132,6 +132,7 @@
<Reference Include="WindowsBase" />
</ItemGroup>
<ItemGroup>
+ <Compile Include="ActivationForViewFetcher.cs" />
<Compile Include="BindingTypeConverters.cs" />
<Compile Include="CommandBinding.cs" />
<Compile Include="CreatesCommandBinding.cs" />
@@ -172,4 +173,4 @@
<Target Name="AfterBuild">
</Target>
-->
-</Project>
+</Project>
View
1 ReactiveUI.Xaml/ReactiveUI.Xaml_SL5.csproj
@@ -89,6 +89,7 @@
<Reference Include="System.Windows.Browser" />
</ItemGroup>
<ItemGroup>
+ <Compile Include="ActivationForViewFetcher.cs" />
<Compile Include="BindingTypeConverters.cs" />
<Compile Include="CommandBinding.cs" />
<Compile Include="CreatesCommandBinding.cs" />
View
1 ReactiveUI.Xaml/ReactiveUI.Xaml_WP7.csproj
@@ -113,6 +113,7 @@
<Reference Include="System.Net" />
</ItemGroup>
<ItemGroup>
+ <Compile Include="ActivationForViewFetcher.cs" />
<Compile Include="BindingTypeConverters.cs" />
<Compile Include="CommandBinding.cs" />
<Compile Include="CreatesCommandBinding.cs" />
View
1 ReactiveUI.Xaml/ReactiveUI.Xaml_WP8.csproj
@@ -91,6 +91,7 @@
<Reference Include="System.Net" />
</ItemGroup>
<ItemGroup>
+ <Compile Include="ActivationForViewFetcher.cs" />
<Compile Include="BindingTypeConverters.cs" />
<Compile Include="CommandBinding.cs" />
<Compile Include="CreatesCommandBinding.cs" />
View
1 ReactiveUI.Xaml/ReactiveUI.Xaml_WinRT.csproj
@@ -36,6 +36,7 @@
<DocumentationFile>bin\Release\WinRT45\ReactiveUI.Xaml_WinRT.xml</DocumentationFile>
</PropertyGroup>
<ItemGroup>
+ <Compile Include="ActivationForViewFetcher.cs" />
<Compile Include="BindingTypeConverters.cs" />
<Compile Include="CommandBinding.cs" />
<Compile Include="CreatesCommandBinding.cs" />
View
1 ReactiveUI.Xaml/ServiceLocationRegistration.cs
@@ -27,6 +27,7 @@ public void Register()
RxApp.Register(typeof (CreatesCommandBindingViaCommandParameter), typeof(ICreatesCommandBinding));
RxApp.Register(typeof (CreatesCommandBindingViaEvent), typeof(ICreatesCommandBinding));
RxApp.Register(typeof (BooleanToVisibilityTypeConverter), typeof (IBindingTypeConverter));
+ RxApp.Register(typeof(ActivationForViewFetcher), typeof(IActivationForViewFetcher));
#endif
#if WINRT
View
131 ReactiveUI/Activation.cs
@@ -0,0 +1,131 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reactive;
+using System.Reactive.Disposables;
+using System.Reactive.Linq;
+using System.Reflection;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace ReactiveUI
+{
+ public interface ISupportsActivation
+ {
+ ViewModelActivator Activator { get; }
+ }
+
+ public interface IActivationForViewFetcher
+ {
+ int GetAffinityForView(Type view);
+ Tuple<IObservable<Unit>, IObservable<Unit>> GetActivationForView(IViewFor view);
+ }
+
+ public sealed class ViewModelActivator
+ {
+ readonly List<Func<IEnumerable<IDisposable>>> blocks;
+ IDisposable activationHandle = Disposable.Empty;
+
+ public ViewModelActivator()
+ {
+ blocks = new List<Func<IEnumerable<IDisposable>>>();
+ }
+
+ internal void addActivationBlock(Func<IEnumerable<IDisposable>> block)
+ {
+ blocks.Add(block);
+ }
+
+ public IDisposable Activate()
+ {
+ var disp = new CompositeDisposable(blocks.SelectMany(x => x()));
+
+ Interlocked.Exchange(ref activationHandle, disp).Dispose();
+ return Disposable.Create(Deactivate);
+ }
+
+ public void Deactivate()
+ {
+ Interlocked.Exchange(ref activationHandle, Disposable.Empty).Dispose();
+ }
+ }
+
+ public static class ViewForMixins
+ {
+ public static void WhenActivated(this ISupportsActivation This, Func<IEnumerable<IDisposable>> block)
+ {
+ This.Activator.addActivationBlock(block);
+ }
+
+ public static void WhenActivated(this ISupportsActivation This, Action<Action<IDisposable>> block)
+ {
+ This.Activator.addActivationBlock(() => {
+ var ret = new List<IDisposable>();
+ block(ret.Add);
+ return ret;
+ });
+ }
+
+ public static IDisposable WithActivation(this ISupportsActivation This)
+ {
+ This.Activator.Activate();
+ return Disposable.Create(This.Activator.Deactivate);
+ }
+
+ public static IDisposable WhenActivated(this IViewFor This, Func<IEnumerable<IDisposable>> block)
+ {
+ var activationFetcher = activationFetcherCache.Get(This.GetType());
+ if (activationFetcher == null) {
+ throw new ArgumentException(
+ String.Format("Don't know how to detect when {0} is activated/deactivated, you may need to implement IActivationForViewFetcher",
+ This.GetType().FullName));
+ }
+
+ var activationEvents = activationFetcher.GetActivationForView(This);
+
+ var viewDisposable = new SerialDisposable();
+
+ return new CompositeDisposable(
+ activationEvents.Item1.Subscribe(_ => viewDisposable.Disposable = new CompositeDisposable(block())),
+ activationEvents.Item2.Subscribe(_ => viewDisposable.Disposable = Disposable.Empty),
+ handleViewModelActivation(This, activationEvents),
+ viewDisposable);
+ }
+
+ static IDisposable handleViewModelActivation(IViewFor view, Tuple<IObservable<Unit>, IObservable<Unit>> activation)
+ {
+ var vm = view.ViewModel as ISupportsActivation;
+ var disp = new SerialDisposable() { Disposable = (vm != null ? vm.Activator.Activate() : Disposable.Empty) };
+
+ var latestVm = Observable.Merge(
+ activation.Item1.Select(_ => view.WhenAny(x => x.ViewModel, x => x.Value)),
+ activation.Item2.Select(_ => Observable.Never<object>().StartWith(default(object))))
+ .Switch()
+ .Select(x => x as ISupportsActivation);
+
+ return new CompositeDisposable(
+ disp,
+ latestVm.Subscribe(x => disp.Disposable =
+ (x != null ? x.Activator.Activate() : Disposable.Empty)));
+ }
+
+ public static IDisposable WhenActivated(this IViewFor This, Action<Action<IDisposable>> block)
+ {
+ return This.WhenActivated(() => {
+ var ret = new List<IDisposable>();
+ block(ret.Add);
+ return ret;
+ });
+ }
+
+ static readonly MemoizingMRUCache<Type, IActivationForViewFetcher> activationFetcherCache =
+ new MemoizingMRUCache<Type, IActivationForViewFetcher>((t, _) => {
+ return RxApp.GetAllServices<IActivationForViewFetcher>()
+ .Aggregate(Tuple.Create(0, default(IActivationForViewFetcher)), (acc, x) => {
+ int score = x.GetAffinityForView(t);
+ return (score > acc.Item1) ? Tuple.Create(score, x) : acc;
+ }).Item2;
+ }, 32);
+ }
+}
View
3 ReactiveUI/ReactiveUI.csproj
@@ -124,6 +124,7 @@
<Reference Include="Microsoft.CSharp" />
</ItemGroup>
<ItemGroup>
+ <Compile Include="Activation.cs" />
<Compile Include="BindingTypeConverters.cs" />
<Compile Include="CompatMixins.cs" />
<Compile Include="DefaultPropertyBinding.cs" />
@@ -188,4 +189,4 @@
<Target Name="AfterBuild">
</Target>
-->
-</Project>
+</Project>
View
1 ReactiveUI/ReactiveUI_Net45.csproj
@@ -119,6 +119,7 @@
<Reference Include="Microsoft.CSharp" />
</ItemGroup>
<ItemGroup>
+ <Compile Include="Activation.cs" />
<Compile Include="BindingTypeConverters.cs" />
<Compile Include="CollectionDebugView.cs" />
<Compile Include="CompatMixins.cs" />
View
3 ReactiveUI/ReactiveUI_SL5.csproj
@@ -103,6 +103,7 @@
<Reference Include="System.Windows.Browser" />
</ItemGroup>
<ItemGroup>
+ <Compile Include="Activation.cs" />
<Compile Include="BindingTypeConverters.cs" />
<Compile Include="CollectionDebugView.cs" />
<Compile Include="CompatMixins.cs" />
@@ -167,4 +168,4 @@
<Target Name="AfterBuild">
</Target>
-->
-</Project>
+</Project>
View
3 ReactiveUI/ReactiveUI_WP7.csproj
@@ -112,6 +112,7 @@
<Reference Include="System.Net" />
</ItemGroup>
<ItemGroup>
+ <Compile Include="Activation.cs" />
<Compile Include="BindingTypeConverters.cs" />
<Compile Include="CollectionDebugView.cs" />
<Compile Include="CompatMixins.cs" />
@@ -175,4 +176,4 @@
<Target Name="AfterBuild">
</Target>
-->
-</Project>
+</Project>
View
1 ReactiveUI/ReactiveUI_WP8.csproj
@@ -104,6 +104,7 @@
<Reference Include="System.Net" />
</ItemGroup>
<ItemGroup>
+ <Compile Include="Activation.cs" />
<Compile Include="BindingTypeConverters.cs" />
<Compile Include="CollectionDebugView.cs" />
<Compile Include="CompatMixins.cs" />
View
3 ReactiveUI/ReactiveUI_WinRT.csproj
@@ -36,6 +36,7 @@
<DocumentationFile>bin\Release\WinRT45\ReactiveUI_WinRT.xml</DocumentationFile>
</PropertyGroup>
<ItemGroup>
+ <Compile Include="Activation.cs" />
<Compile Include="BindingTypeConverters.cs" />
<Compile Include="CollectionDebugView.cs" />
<Compile Include="CompatMixins.cs" />
@@ -115,4 +116,4 @@
<Target Name="AfterBuild">
</Target>
-->
-</Project>
+</Project>

0 comments on commit ff1d1e7

Please sign in to comment.
Something went wrong with that request. Please try again.