diff --git a/build.cake b/build.cake index 24f529ee4a..56653109ae 100644 --- a/build.cake +++ b/build.cake @@ -170,6 +170,7 @@ Task("Run-Unit-Tests-Impl") RunCoreTest("./tests/Avalonia.Styling.UnitTests", data.Parameters, false); RunCoreTest("./tests/Avalonia.Visuals.UnitTests", data.Parameters, false); RunCoreTest("./tests/Avalonia.Skia.UnitTests", data.Parameters, false); + RunCoreTest("./tests/Avalonia.ReactiveUI.UnitTests", data.Parameters, false); if (data.Parameters.IsRunningOnWindows) { RunCoreTest("./tests/Avalonia.Direct2D1.UnitTests", data.Parameters, false); diff --git a/build/ReactiveUI.props b/build/ReactiveUI.props index acdfdd215a..1208be34b8 100644 --- a/build/ReactiveUI.props +++ b/build/ReactiveUI.props @@ -1,5 +1,5 @@ - + diff --git a/samples/VirtualizationDemo/ViewModels/MainWindowViewModel.cs b/samples/VirtualizationDemo/ViewModels/MainWindowViewModel.cs index eb08ef9656..eba17f92e4 100644 --- a/samples/VirtualizationDemo/ViewModels/MainWindowViewModel.cs +++ b/samples/VirtualizationDemo/ViewModels/MainWindowViewModel.cs @@ -7,6 +7,7 @@ using System.Linq; using Avalonia.Collections; using Avalonia.Controls; using Avalonia.Controls.Primitives; +using ReactiveUI.Legacy; using ReactiveUI; namespace VirtualizationDemo.ViewModels diff --git a/src/Avalonia.ReactiveUI/AppBuilderExtensions.cs b/src/Avalonia.ReactiveUI/AppBuilderExtensions.cs index 3eab54115a..d763febdf3 100644 --- a/src/Avalonia.ReactiveUI/AppBuilderExtensions.cs +++ b/src/Avalonia.ReactiveUI/AppBuilderExtensions.cs @@ -4,6 +4,7 @@ using Avalonia.Controls; using Avalonia.Threading; using ReactiveUI; +using Splat; namespace Avalonia { @@ -15,6 +16,9 @@ namespace Avalonia return builder.AfterSetup(_ => { RxApp.MainThreadScheduler = AvaloniaScheduler.Instance; + Locator.CurrentMutable.Register( + () => new AvaloniaActivationForViewFetcher(), + typeof(IActivationForViewFetcher)); }); } } diff --git a/src/Avalonia.ReactiveUI/AvaloniaActivationForViewFetcher.cs b/src/Avalonia.ReactiveUI/AvaloniaActivationForViewFetcher.cs new file mode 100644 index 0000000000..828d8024e6 --- /dev/null +++ b/src/Avalonia.ReactiveUI/AvaloniaActivationForViewFetcher.cs @@ -0,0 +1,38 @@ +// Copyright (c) The Avalonia Project. All rights reserved. +// Licensed under the MIT license. See licence.md file in the project root for full license information. + +using System; +using System.Reflection; +using System.Reactive.Linq; +using Avalonia; +using Avalonia.VisualTree; +using ReactiveUI; + +namespace Avalonia +{ + public class AvaloniaActivationForViewFetcher : IActivationForViewFetcher + { + public int GetAffinityForView(Type view) + { + return typeof(IVisual).GetTypeInfo().IsAssignableFrom(view.GetTypeInfo()) ? 10 : 0; + } + + public IObservable GetActivationForView(IActivatable view) + { + if (!(view is IVisual visual)) return Observable.Return(false); + var viewLoaded = Observable + .FromEventPattern( + x => visual.AttachedToVisualTree += x, + x => visual.DetachedFromVisualTree -= x) + .Select(args => true); + var viewUnloaded = Observable + .FromEventPattern( + x => visual.DetachedFromVisualTree += x, + x => visual.DetachedFromVisualTree -= x) + .Select(args => false); + return viewLoaded + .Merge(viewUnloaded) + .DistinctUntilChanged(); + } + } +} \ No newline at end of file diff --git a/tests/Avalonia.ReactiveUI.UnitTests/Avalonia.ReactiveUI.UnitTests.csproj b/tests/Avalonia.ReactiveUI.UnitTests/Avalonia.ReactiveUI.UnitTests.csproj new file mode 100644 index 0000000000..7c0ff79183 --- /dev/null +++ b/tests/Avalonia.ReactiveUI.UnitTests/Avalonia.ReactiveUI.UnitTests.csproj @@ -0,0 +1,14 @@ + + + netcoreapp2.0 + + + + + + + + + + + diff --git a/tests/Avalonia.ReactiveUI.UnitTests/AvaloniaActivationForViewFetcherTest.cs b/tests/Avalonia.ReactiveUI.UnitTests/AvaloniaActivationForViewFetcherTest.cs new file mode 100644 index 0000000000..97701f8437 --- /dev/null +++ b/tests/Avalonia.ReactiveUI.UnitTests/AvaloniaActivationForViewFetcherTest.cs @@ -0,0 +1,89 @@ +using System; +using System.Reactive.Concurrency; +using System.Reactive.Disposables; +using Avalonia.Controls; +using Avalonia.Rendering; +using Avalonia.Platform; +using Avalonia.UnitTests; +using Avalonia; +using ReactiveUI; +using DynamicData; +using Xunit; +using Splat; + +namespace Avalonia +{ + public class AvaloniaActivationForViewFetcherTest + { + public class TestUserControl : UserControl, IActivatable { } + + public class TestUserControlWithWhenActivated : UserControl, IActivatable + { + public bool Active { get; private set; } + + public TestUserControlWithWhenActivated() + { + this.WhenActivated(disposables => { + Active = true; + Disposable + .Create(() => Active = false) + .DisposeWith(disposables); + }); + } + } + + [Fact] + public void Visual_Element_Is_Activated_And_Deactivated() + { + var userControl = new TestUserControl(); + var activationForViewFetcher = new AvaloniaActivationForViewFetcher(); + + activationForViewFetcher + .GetActivationForView(userControl) + .ToObservableChangeSet(scheduler: ImmediateScheduler.Instance) + .Bind(out var activated) + .Subscribe(); + + var fakeRenderedDecorator = new TestRoot(); + fakeRenderedDecorator.Child = userControl; + Assert.True(activated[0]); + Assert.Equal(1, activated.Count); + + fakeRenderedDecorator.Child = null; + Assert.True(activated[0]); + Assert.False(activated[1]); + Assert.Equal(2, activated.Count); + } + + [Fact] + public void Get_Affinity_For_View_Should_Return_Non_Zero_For_Visual_Elements() + { + var userControl = new TestUserControl(); + var activationForViewFetcher = new AvaloniaActivationForViewFetcher(); + + var forUserControl = activationForViewFetcher.GetAffinityForView(userControl.GetType()); + var forNonUserControl = activationForViewFetcher.GetAffinityForView(typeof(object)); + + Assert.NotEqual(0, forUserControl); + Assert.Equal(0, forNonUserControl); + } + + [Fact] + public void Activation_For_View_Fetcher_Should_Support_When_Activated() + { + Locator.CurrentMutable.RegisterConstant( + new AvaloniaActivationForViewFetcher(), + typeof(IActivationForViewFetcher)); + + var userControl = new TestUserControlWithWhenActivated(); + Assert.False(userControl.Active); + + var fakeRenderedDecorator = new TestRoot(); + fakeRenderedDecorator.Child = userControl; + Assert.True(userControl.Active); + + fakeRenderedDecorator.Child = null; + Assert.False(userControl.Active); + } + } +} \ No newline at end of file