From 3b97bccf8986763bb6872940571d56eb7631433d Mon Sep 17 00:00:00 2001 From: Olly Levett Date: Wed, 8 Feb 2017 20:38:31 +0000 Subject: [PATCH 1/2] style: tidy up comments for ReactiveBinding and command binding (#1267) --- src/ReactiveUI/CommandBinding.cs | 39 +++++++++++++++++++++++++++++-- src/ReactiveUI/ReactiveBinding.cs | 16 ++----------- 2 files changed, 39 insertions(+), 16 deletions(-) diff --git a/src/ReactiveUI/CommandBinding.cs b/src/ReactiveUI/CommandBinding.cs index f393a899ec..3509f103a7 100755 --- a/src/ReactiveUI/CommandBinding.cs +++ b/src/ReactiveUI/CommandBinding.cs @@ -9,6 +9,9 @@ namespace ReactiveUI { + /// + /// Various helpers to bind View controls and ViewModel commands together + /// public static class CommandBinder { static ICommandBinderImplementation binderImplementation; @@ -30,7 +33,7 @@ static CommandBinder() /// The View /// The View model /// The name of the control on the view - /// The ViewModel command to Bind. + /// The ViewModel command to bind. /// The ViewModel property to pass as the /// param of the ICommand /// If specified, bind to the specific event @@ -57,6 +60,7 @@ static CommandBinder() /// the binding /// The View /// The View model + /// The ViewModel command to bind /// The name of the control on the view /// If specified, bind to the specific event /// instead of the default. @@ -81,6 +85,7 @@ static CommandBinder() /// the binding /// The View /// The View model + /// The ViewModel command to bind /// The name of the control on the view /// The ViewModel property to pass as the /// param of the ICommand @@ -126,8 +131,25 @@ interface ICommandBinderImplementation : IEnableLogger where TProp : ICommand; } + /// + /// Used by the CommandBinder extension methods to handle binding View controls and ViewModel commands + /// public class CommandBinderImplementation : ICommandBinderImplementation { + /// + /// Bind a command from the ViewModel to an explicitly specified control + /// on the View. + /// + /// A class representing the binding. Dispose it to disconnect + /// the binding + /// The View + /// The View model + /// The name of the control on the view + /// The ViewModel command to bind + /// The ViewModel property to pass as the + /// param of the ICommand + /// If specified, bind to the specific event + /// instead of the default. public IReactiveBinding BindCommand( TViewModel viewModel, TView view, @@ -157,6 +179,20 @@ public class CommandBinderImplementation : ICommandBinderImplementation source, BindingDirection.OneWay, bindingDisposable); } + /// + /// Bind a command from the ViewModel to an explicitly specified control + /// on the View. + /// + /// A class representing the binding. Dispose it to disconnect + /// the binding + /// The View + /// The View model + /// The name of the control on the view + /// The ViewModel command to bind + /// The ViewModel property to pass as the + /// param of the ICommand + /// If specified, bind to the specific event + /// instead of the default. public IReactiveBinding BindCommand( TViewModel viewModel, TView view, @@ -303,7 +339,6 @@ public static IDisposable BindCommandToObject(ICommand command, object target, I var mi = binder.GetType().GetTypeInfo().DeclaredMethods.First(x => x.Name == "BindCommandToObject" && x.IsGenericMethod); mi = mi.MakeGenericMethod(new[] {eventArgsType}); - //var ret = binder.BindCommandToObject(command, target, commandParameter, eventName); var ret = (IDisposable) mi.Invoke(binder, new[] {command, target, commandParameter, eventName}); if (ret == null) { throw new Exception(String.Format("Couldn't bind Command Binder for {0} and event {1}", type.FullName, eventName)); diff --git a/src/ReactiveUI/ReactiveBinding.cs b/src/ReactiveUI/ReactiveBinding.cs index b4dfcd04bc..7ee103a601 100644 --- a/src/ReactiveUI/ReactiveBinding.cs +++ b/src/ReactiveUI/ReactiveBinding.cs @@ -18,7 +18,7 @@ public interface IReactiveBinding : IDisposable where TView : IViewFor { /// - /// The instance of the view model this binding is applied to. + /// The instance of the view model this binding is applied to. /// /// /// The view model. @@ -76,15 +76,6 @@ internal class ReactiveBinding : IReactiveBinding - /// Initializes a new instance of the class. - /// - /// The view. - /// The view model. - /// The view path. - /// The view model path. - /// The direction. - /// The binding disposable. public ReactiveBinding(TView view, TViewModel viewModel, Expression viewExpression, Expression viewModelExpression, IObservable changed, BindingDirection direction, IDisposable bindingDisposable) { @@ -99,7 +90,7 @@ internal class ReactiveBinding : IReactiveBinding - /// The instance of the view model this binding is applied to. + /// The instance of the view model this binding is applied to. /// /// /// The view model. @@ -150,9 +141,6 @@ internal class ReactiveBinding : IReactiveBinding public BindingDirection Direction { get; private set; } - /// - /// Releases unmanaged and - optionally - managed resources. - /// public void Dispose() { if (bindingDisposable != null) { From 8e9963f05ab38cf13d158240d2f1a6bc5d6ecbb2 Mon Sep 17 00:00:00 2001 From: Olly Levett Date: Sat, 11 Feb 2017 08:30:55 +0000 Subject: [PATCH 2/2] test: add activation tests (#1269) --- .../{ActivationTest.cs => ActivationTests.cs} | 95 +++++++++++++++++-- .../ReactiveUI.Tests_Android.csproj | 2 +- .../ReactiveUI.Tests_Net45.csproj | 2 +- .../ReactiveUI.Tests_iOS.csproj | 2 +- 4 files changed, 90 insertions(+), 11 deletions(-) rename src/ReactiveUI.Tests/{ActivationTest.cs => ActivationTests.cs} (75%) diff --git a/src/ReactiveUI.Tests/ActivationTest.cs b/src/ReactiveUI.Tests/ActivationTests.cs similarity index 75% rename from src/ReactiveUI.Tests/ActivationTest.cs rename to src/ReactiveUI.Tests/ActivationTests.cs index c900db5b02..5c024a2f37 100644 --- a/src/ReactiveUI.Tests/ActivationTest.cs +++ b/src/ReactiveUI.Tests/ActivationTests.cs @@ -1,12 +1,9 @@ using System; -using System.Collections.Generic; using System.Linq; using System.Reactive; using System.Reactive.Disposables; using System.Reactive.Linq; using System.Reactive.Subjects; -using System.Text; -using System.Threading.Tasks; using Splat; using Xunit; @@ -21,7 +18,7 @@ public class ActivatingViewModel : ReactiveObject, ISupportsActivation public ActivatingViewModel() { Activator = new ViewModelActivator(); - + this.WhenActivated(d => { IsActiveCount++; d(Disposable.Create(() => IsActiveCount--)); @@ -45,12 +42,14 @@ public DerivedActivatingViewModel() public class ActivatingView : ReactiveObject, IViewFor { ActivatingViewModel viewModel; - public ActivatingViewModel ViewModel { + public ActivatingViewModel ViewModel + { get { return viewModel; } set { this.RaiseAndSetIfChanged(ref viewModel, value); } } - object IViewFor.ViewModel { + object IViewFor.ViewModel + { get { return ViewModel; } set { ViewModel = (ActivatingViewModel)value; } } @@ -248,8 +247,7 @@ public void CanUnloadAndLoadViewAgain() locator.InitializeReactiveUI(); locator.Register(() => new ActivatingViewFetcher(), typeof(IActivationForViewFetcher)); - using (locator.WithResolver()) - { + using (locator.WithResolver()) { var vm = new ActivatingViewModel(); var fixture = new ActivatingView(); @@ -270,6 +268,87 @@ public void CanUnloadAndLoadViewAgain() Assert.Equal(1, fixture.IsActiveCount); } } + } + + public class ViewModelActivatorTests + { + [Fact] + public void ActivatingTicksActivatedObservable() + { + var viewModelActivator = new ViewModelActivator(); + var activated = viewModelActivator.Activated.CreateCollection(); + + viewModelActivator.Activate(); + + Assert.Equal(1, activated.Count); + } + + [Fact] + public void DeactivatingIgnoringRefCountTicksDeactivatedObservable() + { + var viewModelActivator = new ViewModelActivator(); + var deactivated = viewModelActivator.Deactivated.CreateCollection(); + + viewModelActivator.Deactivate(true); + + Assert.Equal(1, deactivated.Count); + } + + [Fact] + public void DeactivatingCountDoesntTickDeactivatedObservable() + { + var viewModelActivator = new ViewModelActivator(); + var deactivated = viewModelActivator.Deactivated.CreateCollection(); + + viewModelActivator.Deactivate(false); + + Assert.Equal(0, deactivated.Count); + } + [Fact] + public void DeactivatingFollowingActivatingTicksDeactivatedObservable() + { + var viewModelActivator = new ViewModelActivator(); + var deactivated = viewModelActivator.Deactivated.CreateCollection(); + + viewModelActivator.Activate(); + viewModelActivator.Deactivate(false); + + Assert.Equal(1, deactivated.Count); + } + } + + public class CanActivateViewFetcherTests + { + private class CanActivateStub : ICanActivate + { + public IObservable Activated { get; } + + public IObservable Deactivated { get; } + } + + [Fact] + public void ReturnsPositiveForICanActivate() + { + var canActivateViewFetcher = new CanActivateViewFetcher(); + var affinity = canActivateViewFetcher.GetAffinityForView(typeof(ICanActivate)); + Assert.True(affinity > 0); + } + + [Fact] + public void ReturnsPositiveForICanActivateDerivatives() + { + var canActivateViewFetcher = new CanActivateViewFetcher(); + var affinity = canActivateViewFetcher.GetAffinityForView(typeof(CanActivateStub)); + Assert.True(affinity > 0); + } + + [Fact] + public void ReturnsZeroForNonICanActivateDerivatives() + { + var canActivateViewFetcher = new CanActivateViewFetcher(); + var affinity = canActivateViewFetcher.GetAffinityForView(typeof(CanActivateViewFetcherTests)); + Assert.Equal(0, affinity); + } } } diff --git a/src/ReactiveUI.Tests/ReactiveUI.Tests_Android.csproj b/src/ReactiveUI.Tests/ReactiveUI.Tests_Android.csproj index 75b91de818..5005bf5627 100644 --- a/src/ReactiveUI.Tests/ReactiveUI.Tests_Android.csproj +++ b/src/ReactiveUI.Tests/ReactiveUI.Tests_Android.csproj @@ -136,7 +136,7 @@ - + diff --git a/src/ReactiveUI.Tests/ReactiveUI.Tests_Net45.csproj b/src/ReactiveUI.Tests/ReactiveUI.Tests_Net45.csproj index 531e6db9e4..7db5a632eb 100644 --- a/src/ReactiveUI.Tests/ReactiveUI.Tests_Net45.csproj +++ b/src/ReactiveUI.Tests/ReactiveUI.Tests_Net45.csproj @@ -97,7 +97,7 @@ - + diff --git a/src/ReactiveUI.Tests/ReactiveUI.Tests_iOS.csproj b/src/ReactiveUI.Tests/ReactiveUI.Tests_iOS.csproj index b1d63cc925..bdfae1fa25 100644 --- a/src/ReactiveUI.Tests/ReactiveUI.Tests_iOS.csproj +++ b/src/ReactiveUI.Tests/ReactiveUI.Tests_iOS.csproj @@ -170,7 +170,7 @@ - +