From 751ef25c45dfb2340894955601ecd4cc5f897858 Mon Sep 17 00:00:00 2001 From: Chris Pulman Date: Tue, 29 Jun 2021 05:13:01 +0100 Subject: [PATCH 1/4] feature for Xamarin added ReactiveShell, ShellViewModel, ReactiveFlyOut ReactiveShell can be used when you wish to use a viewmodel on your base NOTE: Only supports WhenActivated NOT WhenDeactivated due to the Base nature of the Shell component ShellViewModel is a ViewModel based version of ShellContent --- .../ActivatingReactiveContentPageTests.cs | 116 +++++++++ .../Activation/Mocks/ActivatingViewModel2.cs | 41 +++ .../Activation/Mocks/App.xaml | 34 +++ .../Activation/Mocks/App.xaml.cs | 57 +++++ .../Activation/Mocks/AppShell.xaml | 39 +++ .../Activation/Mocks/AppShell.xaml.cs | 35 +++ .../Activation/Mocks/ApplicationMock.cs | 23 ++ .../Activation/Mocks/CarouselPageView.cs | 30 +++ .../Activation/Mocks/CarouselPageViewModel.cs | 39 +++ .../Activation/Mocks/ContentPageView.cs | 31 +++ .../Activation/Mocks/ContentPageViewModel.cs | 41 +++ .../Activation/Mocks/FlyOutPageViewModel.cs | 41 +++ .../Activation/Mocks/FlyoutPageView.xaml | 20 ++ .../Activation/Mocks/FlyoutPageView.xaml.cs | 57 +++++ .../Mocks/FlyoutPageViewDetail.xaml | 10 + .../Mocks/FlyoutPageViewDetail.xaml.cs | 26 ++ .../Mocks/FlyoutPageViewFlyout.xaml | 48 ++++ .../Mocks/FlyoutPageViewFlyout.xaml.cs | 81 ++++++ .../Mocks/FlyoutPageViewFlyoutMenuItem.cs | 47 ++++ .../Activation/Mocks/MockForms.cs | 241 ++++++++++++++++++ .../Activation/Mocks/ShellView.cs | 34 +++ .../Activation/Mocks/ShellViewModel.cs | 41 +++ .../Activation/Mocks/TabbedPageView.cs | 30 +++ .../Activation/Mocks/TabbedPageViewModel.cs | 41 +++ .../ReactiveUI.XamForms.Tests.csproj | 30 +++ .../ActivationForViewFetcher.cs | 26 ++ src/ReactiveUI.XamForms/ReactiveFlyoutPage.cs | 55 ++++ .../ReactiveMasterDetailPage.cs | 2 + src/ReactiveUI.XamForms/ReactiveShell.cs | 55 ++++ src/ReactiveUI.XamForms/ShellViewModel.cs | 89 +++++++ 30 files changed, 1460 insertions(+) create mode 100644 src/ReactiveUI.XamForms.Tests/Activation/ActivatingReactiveContentPageTests.cs create mode 100644 src/ReactiveUI.XamForms.Tests/Activation/Mocks/ActivatingViewModel2.cs create mode 100644 src/ReactiveUI.XamForms.Tests/Activation/Mocks/App.xaml create mode 100644 src/ReactiveUI.XamForms.Tests/Activation/Mocks/App.xaml.cs create mode 100644 src/ReactiveUI.XamForms.Tests/Activation/Mocks/AppShell.xaml create mode 100644 src/ReactiveUI.XamForms.Tests/Activation/Mocks/AppShell.xaml.cs create mode 100644 src/ReactiveUI.XamForms.Tests/Activation/Mocks/ApplicationMock.cs create mode 100644 src/ReactiveUI.XamForms.Tests/Activation/Mocks/CarouselPageView.cs create mode 100644 src/ReactiveUI.XamForms.Tests/Activation/Mocks/CarouselPageViewModel.cs create mode 100644 src/ReactiveUI.XamForms.Tests/Activation/Mocks/ContentPageView.cs create mode 100644 src/ReactiveUI.XamForms.Tests/Activation/Mocks/ContentPageViewModel.cs create mode 100644 src/ReactiveUI.XamForms.Tests/Activation/Mocks/FlyOutPageViewModel.cs create mode 100644 src/ReactiveUI.XamForms.Tests/Activation/Mocks/FlyoutPageView.xaml create mode 100644 src/ReactiveUI.XamForms.Tests/Activation/Mocks/FlyoutPageView.xaml.cs create mode 100644 src/ReactiveUI.XamForms.Tests/Activation/Mocks/FlyoutPageViewDetail.xaml create mode 100644 src/ReactiveUI.XamForms.Tests/Activation/Mocks/FlyoutPageViewDetail.xaml.cs create mode 100644 src/ReactiveUI.XamForms.Tests/Activation/Mocks/FlyoutPageViewFlyout.xaml create mode 100644 src/ReactiveUI.XamForms.Tests/Activation/Mocks/FlyoutPageViewFlyout.xaml.cs create mode 100644 src/ReactiveUI.XamForms.Tests/Activation/Mocks/FlyoutPageViewFlyoutMenuItem.cs create mode 100644 src/ReactiveUI.XamForms.Tests/Activation/Mocks/MockForms.cs create mode 100644 src/ReactiveUI.XamForms.Tests/Activation/Mocks/ShellView.cs create mode 100644 src/ReactiveUI.XamForms.Tests/Activation/Mocks/ShellViewModel.cs create mode 100644 src/ReactiveUI.XamForms.Tests/Activation/Mocks/TabbedPageView.cs create mode 100644 src/ReactiveUI.XamForms.Tests/Activation/Mocks/TabbedPageViewModel.cs create mode 100644 src/ReactiveUI.XamForms/ReactiveFlyoutPage.cs create mode 100644 src/ReactiveUI.XamForms/ReactiveShell.cs create mode 100644 src/ReactiveUI.XamForms/ShellViewModel.cs diff --git a/src/ReactiveUI.XamForms.Tests/Activation/ActivatingReactiveContentPageTests.cs b/src/ReactiveUI.XamForms.Tests/Activation/ActivatingReactiveContentPageTests.cs new file mode 100644 index 0000000000..d1db7a1b12 --- /dev/null +++ b/src/ReactiveUI.XamForms.Tests/Activation/ActivatingReactiveContentPageTests.cs @@ -0,0 +1,116 @@ +// Copyright (c) 2021 .NET Foundation and Contributors. All rights reserved. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System.Threading.Tasks; +using ReactiveUI.XamForms; +using ReactiveUI.XamForms.Tests.Activation; +using ReactiveUI.XamForms.Tests.Activation.Mocks; +using Splat; +using Xamarin.Forms; +using Xunit; + +namespace ReactiveUI.Tests +{ + /// + /// Tests for activating views. + /// + public class ActivatingReactiveContentPageTests + { + /// + /// Tests to make sure that views generally activate. + /// + [Fact] + public void ActivatingReactiveContentPageTest() + { + var locator = new ModernDependencyResolver(); + locator.InitializeSplat(); + locator.InitializeReactiveUI(); + locator.Register(() => new ActivationForViewFetcher(), typeof(IActivationForViewFetcher)); + locator.Register>(() => new ShellView()); + locator.Register>(() => new ContentPageView()); + locator.Register>(() => new TabbedPageView()); + locator.Register>(() => new CarouselPageView()); + locator.Register>(() => new FlyoutPageView()); + + MockForms.Init(); + var app = new App(); + var main = app.MainPage as AppShell; + + Assert.Equal(1, main!.ViewModel!.IsActiveCount); + Assert.Equal(1, main.IsActiveCount); + + var vm = new ContentPageViewModel(); + var fixture = new ContentPageView + { + ViewModel = vm + }; + + // Activate + main.Navigation.PushAsync(fixture); + Assert.Equal(1, fixture.ViewModel.IsActiveCount); + Assert.Equal(1, fixture.IsActiveCount); + + // Deactivate + Shell.Current.GoToAsync(".."); + fixture.ViewModel = null; + Assert.Equal(0, vm.IsActiveCount); + Assert.Equal(0, fixture.IsActiveCount); + + var vm1 = new TabbedPageViewModel(); + var fixture1 = new TabbedPageView + { + ViewModel = vm1 + }; + + // Activate + main.Navigation.PushAsync(fixture1); + Assert.Equal(1, fixture1.ViewModel.IsActiveCount); + Assert.Equal(1, fixture1.IsActiveCount); + + // Deactivate + Shell.Current.GoToAsync(".."); + fixture1.ViewModel = null; + Assert.Equal(0, vm1.IsActiveCount); + Assert.Equal(0, fixture1.IsActiveCount); + + var vm3 = new FlyOutPageViewModel(); + var fixture3 = new FlyoutPageView + { + ViewModel = vm3 + }; + + // Activate + main.Navigation.PushAsync(fixture3); + Assert.Equal(1, fixture3.ViewModel!.IsActiveCount); + Assert.Equal(1, fixture3.IsActiveCount); + + // Deactivate + Shell.Current.GoToAsync(".."); + fixture3.ViewModel = null; + Assert.Equal(0, vm3.IsActiveCount); + Assert.Equal(0, fixture3.IsActiveCount); + + var vm4 = new CarouselPageViewModel(); + var fixture4 = new CarouselPageView + { + ViewModel = vm4 + }; + + // Activate + main.Navigation.PushAsync(fixture4); + Assert.Equal(1, fixture4.ViewModel!.IsActiveCount); + Assert.Equal(1, fixture4.IsActiveCount); + + // Deactivate + Shell.Current.GoToAsync(".."); + fixture4.ViewModel = null; + Assert.Equal(0, vm4.IsActiveCount); + Assert.Equal(0, fixture4.IsActiveCount); + + // remember to kill the app + app.Quit(); + } + } +} diff --git a/src/ReactiveUI.XamForms.Tests/Activation/Mocks/ActivatingViewModel2.cs b/src/ReactiveUI.XamForms.Tests/Activation/Mocks/ActivatingViewModel2.cs new file mode 100644 index 0000000000..fa94722b57 --- /dev/null +++ b/src/ReactiveUI.XamForms.Tests/Activation/Mocks/ActivatingViewModel2.cs @@ -0,0 +1,41 @@ +// Copyright (c) 2021 .NET Foundation and Contributors. All rights reserved. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System.Reactive.Disposables; + +namespace ReactiveUI.XamForms.Tests.Activation +{ + /// + /// Activating View Model2. + /// + /// + /// + public class ActivatingViewModel2 : ReactiveObject, IActivatableViewModel + { + /// + /// Initializes a new instance of the class. + /// + public ActivatingViewModel2() + { + Activator = new ViewModelActivator(); + + this.WhenActivated(d => + { + IsActiveCount++; + d(Disposable.Create(() => IsActiveCount--)); + }); + } + + /// + /// Gets or sets the Activator which will be used by the View when Activation/Deactivation occurs. + /// + public ViewModelActivator Activator { get; protected set; } + + /// + /// Gets or sets the active count. + /// + public int IsActiveCount { get; protected set; } + } +} diff --git a/src/ReactiveUI.XamForms.Tests/Activation/Mocks/App.xaml b/src/ReactiveUI.XamForms.Tests/Activation/Mocks/App.xaml new file mode 100644 index 0000000000..393be06d9b --- /dev/null +++ b/src/ReactiveUI.XamForms.Tests/Activation/Mocks/App.xaml @@ -0,0 +1,34 @@ + + + + + + #2196F3 + + + + diff --git a/src/ReactiveUI.XamForms.Tests/Activation/Mocks/App.xaml.cs b/src/ReactiveUI.XamForms.Tests/Activation/Mocks/App.xaml.cs new file mode 100644 index 0000000000..e26216449e --- /dev/null +++ b/src/ReactiveUI.XamForms.Tests/Activation/Mocks/App.xaml.cs @@ -0,0 +1,57 @@ +// Copyright (c) 2021 .NET Foundation and Contributors. All rights reserved. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using Xamarin.Forms; + +namespace ReactiveUI.XamForms.Tests.Activation +{ + /// + /// The App. + /// + /// + public partial class App + { + /// + /// Initializes a new instance of the class. + /// + /// The page. + public App() + { + InitializeComponent(); + + MainPage = new AppShell(); + } + + /// + /// Application developers override this method to perform actions when the application starts. + /// + /// + /// To be added. + /// + protected override void OnStart() + { + } + + /// + /// Application developers override this method to perform actions when the application enters the sleeping state. + /// + /// + /// To be added. + /// + protected override void OnSleep() + { + } + + /// + /// Application developers override this method to perform actions when the application resumes from a sleeping state. + /// + /// + /// To be added. + /// + protected override void OnResume() + { + } + } +} diff --git a/src/ReactiveUI.XamForms.Tests/Activation/Mocks/AppShell.xaml b/src/ReactiveUI.XamForms.Tests/Activation/Mocks/AppShell.xaml new file mode 100644 index 0000000000..18c67fc8a0 --- /dev/null +++ b/src/ReactiveUI.XamForms.Tests/Activation/Mocks/AppShell.xaml @@ -0,0 +1,39 @@ + + + + + + + +