From 834d964953b1a1b3ac54361cf7a8247949e0e7ae Mon Sep 17 00:00:00 2001 From: Glenn Watson Date: Wed, 7 Nov 2018 10:14:50 -0800 Subject: [PATCH 1/4] Initial version of the ReactiveCommand abstract class being removed --- .../ReactiveUI.Fody.Tests.csproj | 6 +- .../ApiApprovalTests.ReactiveUI.approved.txt | 25 ++- src/ReactiveUI.Tests/CommandBindingTests.cs | 22 +-- .../Platforms/winforms/CommandBindingTests.cs | 2 +- src/ReactiveUI.Tests/ReactiveUI.Tests.csproj | 14 +- src/ReactiveUI.Tests/WeakEventManagerTest.cs | 2 +- .../Command/CommandBinderImplementation.cs | 5 +- .../EventManagers/WeakEventManager.cs | 4 +- .../WinRTAutoSuspendApplication.cs | 4 +- .../ReactiveCommand/IReactiveCommand.cs | 33 ++++ .../ReactiveCommand/ReactiveCommand.cs | 148 +----------------- .../ReactiveCommand/ReactiveCommandBase.cs | 141 ++++++++++++++++- 12 files changed, 211 insertions(+), 195 deletions(-) create mode 100644 src/ReactiveUI/ReactiveCommand/IReactiveCommand.cs diff --git a/src/ReactiveUI.Fody.Tests/ReactiveUI.Fody.Tests.csproj b/src/ReactiveUI.Fody.Tests/ReactiveUI.Fody.Tests.csproj index 97f8921f0d..02b11016d6 100644 --- a/src/ReactiveUI.Fody.Tests/ReactiveUI.Fody.Tests.csproj +++ b/src/ReactiveUI.Fody.Tests/ReactiveUI.Fody.Tests.csproj @@ -7,12 +7,12 @@ - - + + - + diff --git a/src/ReactiveUI.Tests/API/ApiApprovalTests.ReactiveUI.approved.txt b/src/ReactiveUI.Tests/API/ApiApprovalTests.ReactiveUI.approved.txt index f638f385fd..54ae8b01fe 100644 --- a/src/ReactiveUI.Tests/API/ApiApprovalTests.ReactiveUI.approved.txt +++ b/src/ReactiveUI.Tests/API/ApiApprovalTests.ReactiveUI.approved.txt @@ -458,13 +458,8 @@ namespace ReactiveUI public string PropertyName { get; set; } } public delegate void PropertyChangingEventHandler(object sender, ReactiveUI.PropertyChangingEventArgs e); - public abstract class ReactiveCommand : ReactiveUI.IHandleObservableErrors, System.IDisposable, System.Windows.Input.ICommand + public class static ReactiveCommand { - protected ReactiveCommand() { } - public abstract System.IObservable CanExecute { get; } - public abstract System.IObservable IsExecuting { get; } - public abstract System.IObservable ThrownExceptions { get; } - public event System.EventHandler System.Windows.Input.ICommand.CanExecuteChanged; public static ReactiveUI.ReactiveCommand Create(System.Action execute, System.IObservable canExecute = null, System.Reactive.Concurrency.IScheduler outputScheduler = null) { } public static ReactiveUI.ReactiveCommand Create(System.Func execute, System.IObservable canExecute = null, System.Reactive.Concurrency.IScheduler outputScheduler = null) { } public static ReactiveUI.ReactiveCommand Create(System.Action execute, System.IObservable canExecute = null, System.Reactive.Concurrency.IScheduler outputScheduler = null) { } @@ -480,11 +475,6 @@ namespace ReactiveUI public static ReactiveUI.ReactiveCommand CreateFromTask(System.Func> execute, System.IObservable canExecute = null, System.Reactive.Concurrency.IScheduler outputScheduler = null) { } public static ReactiveUI.ReactiveCommand CreateFromTask(System.Func execute, System.IObservable canExecute = null, System.Reactive.Concurrency.IScheduler outputScheduler = null) { } public static ReactiveUI.ReactiveCommand CreateFromTask(System.Func execute, System.IObservable canExecute = null, System.Reactive.Concurrency.IScheduler outputScheduler = null) { } - public void Dispose() { } - protected abstract void Dispose(bool disposing); - protected abstract bool ICommandCanExecute(object parameter); - protected abstract void ICommandExecute(object parameter); - protected void OnCanExecuteChanged() { } } public class ReactiveCommand : ReactiveUI.ReactiveCommandBase { @@ -496,12 +486,19 @@ namespace ReactiveUI public override System.IObservable Execute(TParam parameter = null) { } public override System.IDisposable Subscribe(System.IObserver observer) { } } - public abstract class ReactiveCommandBase : ReactiveUI.ReactiveCommand, System.IObservable + public abstract class ReactiveCommandBase : ReactiveUI.IHandleObservableErrors, System.IDisposable, System.IObservable, System.Windows.Input.ICommand { protected ReactiveCommandBase() { } + public abstract System.IObservable CanExecute { get; } + public abstract System.IObservable IsExecuting { get; } + public abstract System.IObservable ThrownExceptions { get; } + public event System.EventHandler System.Windows.Input.ICommand.CanExecuteChanged; + public void Dispose() { } + protected abstract void Dispose(bool disposing); public abstract System.IObservable Execute(TParam parameter = null); - protected override bool ICommandCanExecute(object parameter) { } - protected override void ICommandExecute(object parameter) { } + protected virtual bool ICommandCanExecute(object parameter) { } + protected virtual void ICommandExecute(object parameter) { } + protected void OnCanExecuteChanged() { } public abstract System.IDisposable Subscribe(System.IObserver observer); } public class static ReactiveCommandMixins diff --git a/src/ReactiveUI.Tests/CommandBindingTests.cs b/src/ReactiveUI.Tests/CommandBindingTests.cs index db6ae4e153..5e0ad81564 100755 --- a/src/ReactiveUI.Tests/CommandBindingTests.cs +++ b/src/ReactiveUI.Tests/CommandBindingTests.cs @@ -17,7 +17,7 @@ namespace ReactiveUI.Tests { public class FakeViewModel : ReactiveObject { - public ReactiveCommand Cmd { get; protected set; } + public ReactiveCommand Cmd { get; protected set; } public FakeViewModel() { @@ -123,9 +123,9 @@ public void EventBinderBindsToImplicitEvent() public class CommandBindViewModel : ReactiveObject { - public ReactiveCommand _Command1; + public ReactiveCommand _Command1; - public ReactiveCommand Command1 + public ReactiveCommand Command1 { get => _Command1; set => this.RaiseAndSetIfChanged(ref _Command1, value); @@ -141,7 +141,7 @@ public ReactiveCommand Command2 public CommandBindViewModel() { - Command1 = ReactiveCommand.Create(() => { }); + Command1 = ReactiveCommand.Create(_ => Unit.Default); Command2 = ReactiveCommand.Create(() => { }); } @@ -163,7 +163,7 @@ public FakeNestedViewModel() NestedCommand = ReactiveCommand.Create(() => { }); } - public ReactiveCommand NestedCommand { get; protected set; } + public ReactiveCommand NestedCommand { get; protected set; } } public class CustomClickButton : Button @@ -235,7 +235,7 @@ public void CommandBindByNameWireup() var disp = view.BindCommand(vm, x => x.Command1, x => x.Command1); Assert.Equal(vm.Command1, view.Command1.Command); - var newCmd = ReactiveCommand.Create(() => { }); + var newCmd = ReactiveCommand.Create(_ => { }); vm.Command1 = newCmd; Assert.Equal(newCmd, view.Command1.Command); @@ -265,7 +265,7 @@ public void CommandBindSetsInitialEnabledState_True() var view = new CommandBindView { ViewModel = vm }; var canExecute1 = new BehaviorSubject(true); - var cmd1 = ReactiveCommand.Create(() => { }, canExecute1); + var cmd1 = ReactiveCommand.Create(_ => { }, canExecute1); vm.Command1 = cmd1; var disp = view.BindCommand(vm, x => x.Command1, x => x.Command1); @@ -280,7 +280,7 @@ public void CommandBindSetsDisablesCommandWhenCanExecuteChanged() var view = new CommandBindView { ViewModel = vm }; var canExecute1 = new BehaviorSubject(true); - var cmd1 = ReactiveCommand.Create(() => { }, canExecute1); + var cmd1 = ReactiveCommand.Create(_ => { }, canExecute1); vm.Command1 = cmd1; var disp = view.BindCommand(vm, x => x.Command1, x => x.Command1); @@ -299,7 +299,7 @@ public void CommandBindSetsInitialEnabledState_False() var view = new CommandBindView { ViewModel = vm }; var canExecute1 = new BehaviorSubject(false); - var cmd1 = ReactiveCommand.Create(() => { }, canExecute1); + var cmd1 = ReactiveCommand.Create(_ => { }, canExecute1); vm.Command1 = cmd1; var disp = view.BindCommand(vm, x => x.Command1, x => x.Command1); @@ -314,7 +314,7 @@ public void CommandBindRaisesCanExecuteChangedOnBind() var view = new CommandBindView { ViewModel = vm }; var canExecute1 = new BehaviorSubject(true); - var cmd1 = ReactiveCommand.Create(() => { }, canExecute1); + var cmd1 = ReactiveCommand.Create(_ => { }, canExecute1); vm.Command1 = cmd1; var disp = view.BindCommand(vm, x => x.Command1, x => x.Command1); @@ -323,7 +323,7 @@ public void CommandBindRaisesCanExecuteChangedOnBind() // Now change to a disabled cmd var canExecute2 = new BehaviorSubject(false); - var cmd2 = ReactiveCommand.Create(() => { }, canExecute2); + var cmd2 = ReactiveCommand.Create(_ => { }, canExecute2); vm.Command1 = cmd2; Assert.False(view.Command1.IsEnabled); diff --git a/src/ReactiveUI.Tests/Platforms/winforms/CommandBindingTests.cs b/src/ReactiveUI.Tests/Platforms/winforms/CommandBindingTests.cs index 6cd0e1c677..aed8643d3c 100755 --- a/src/ReactiveUI.Tests/Platforms/winforms/CommandBindingTests.cs +++ b/src/ReactiveUI.Tests/Platforms/winforms/CommandBindingTests.cs @@ -215,7 +215,7 @@ public void CommandBindToExplicitEventWireup() public class FakeViewModel : ReactiveObject { - public ReactiveCommand Cmd { get; protected set; } + public ReactiveCommand Cmd { get; protected set; } public FakeViewModel() { diff --git a/src/ReactiveUI.Tests/ReactiveUI.Tests.csproj b/src/ReactiveUI.Tests/ReactiveUI.Tests.csproj index 6fa4fbf116..164684504d 100644 --- a/src/ReactiveUI.Tests/ReactiveUI.Tests.csproj +++ b/src/ReactiveUI.Tests/ReactiveUI.Tests.csproj @@ -8,17 +8,17 @@ - + - - - - - + + + + + - + diff --git a/src/ReactiveUI.Tests/WeakEventManagerTest.cs b/src/ReactiveUI.Tests/WeakEventManagerTest.cs index a0b8fed1bc..e001f0b9bd 100755 --- a/src/ReactiveUI.Tests/WeakEventManagerTest.cs +++ b/src/ReactiveUI.Tests/WeakEventManagerTest.cs @@ -16,7 +16,7 @@ public class WeakEventManagerTest public void ButtonDoesNotLeakTest() { var button = new Button(); - ReactiveCommand command = ReactiveCommand.Create(() => { }); + var command = ReactiveCommand.Create(() => { }); button.Command = command; var buttonRef = new WeakReference(button); diff --git a/src/ReactiveUI/Bindings/Command/CommandBinderImplementation.cs b/src/ReactiveUI/Bindings/Command/CommandBinderImplementation.cs index 9a54447763..dbbfb84691 100644 --- a/src/ReactiveUI/Bindings/Command/CommandBinderImplementation.cs +++ b/src/ReactiveUI/Bindings/Command/CommandBinderImplementation.cs @@ -56,14 +56,13 @@ public IReactiveBinding BindCommand Observable.Return(withParameter())), toEvent, cmd => { - var rc = cmd as ReactiveCommand; + var rc = cmd as IReactiveCommand; if (rc == null) { return new RelayCommand(cmd.CanExecute, _ => cmd.Execute(withParameter())); } - var ret = ReactiveCommand.Create(() => ((ICommand)rc).Execute(null), rc.CanExecute); - return ret; + return ReactiveCommand.Create(() => ((ICommand)rc).Execute(null), rc.CanExecute); }); return new ReactiveBinding( diff --git a/src/ReactiveUI/EventManagers/WeakEventManager.cs b/src/ReactiveUI/EventManagers/WeakEventManager.cs index 5f8fa574b3..71674cdba6 100644 --- a/src/ReactiveUI/EventManagers/WeakEventManager.cs +++ b/src/ReactiveUI/EventManagers/WeakEventManager.cs @@ -293,11 +293,11 @@ public bool Matches(object source, TEventHandler handler) ReferenceEquals(_source.Target, source) && _originalHandler != null && (ReferenceEquals(_originalHandler.Target, handler) || - _originalHandler.Target is PropertyChangedEventHandler eventHandler && + (_originalHandler.Target is PropertyChangedEventHandler eventHandler && handler is PropertyChangedEventHandler && Equals( eventHandler.Target, - (handler as PropertyChangedEventHandler)?.Target)); + (handler as PropertyChangedEventHandler)?.Target))); } } diff --git a/src/ReactiveUI/Platforms/uap10.0.16299/WinRTAutoSuspendApplication.cs b/src/ReactiveUI/Platforms/uap10.0.16299/WinRTAutoSuspendApplication.cs index 8e48691ea1..1f63c794c7 100644 --- a/src/ReactiveUI/Platforms/uap10.0.16299/WinRTAutoSuspendApplication.cs +++ b/src/ReactiveUI/Platforms/uap10.0.16299/WinRTAutoSuspendApplication.cs @@ -20,8 +20,8 @@ namespace ReactiveUI /// /// AutoSuspend-based Application. To use AutoSuspend with WinRT, change your /// Application to inherit from this class, then call: - /// - /// Locator.Current.GetService.().SetupDefaultSuspendResume(); + /// Locator.Current.GetService.<ISuspensionHost>().SetupDefaultSuspendResume(); + /// This will register your suspension host. /// public class AutoSuspendHelper : IEnableLogger { diff --git a/src/ReactiveUI/ReactiveCommand/IReactiveCommand.cs b/src/ReactiveUI/ReactiveCommand/IReactiveCommand.cs new file mode 100644 index 0000000000..844b8f9c31 --- /dev/null +++ b/src/ReactiveUI/ReactiveCommand/IReactiveCommand.cs @@ -0,0 +1,33 @@ +// 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 more information. + +using System; + +namespace ReactiveUI +{ + /// + /// Encapsulates a user action behind a reactive interface. + /// + internal interface IReactiveCommand : IDisposable, IHandleObservableErrors + { + /// + /// Gets an observable whose value indicates whether the command is currently executing. + /// + /// + /// This observable can be particularly useful for updating UI, such as showing an activity indicator whilst a command + /// is executing. + /// + IObservable IsExecuting { get; } + + /// + /// Gets an observable whose value indicates whether the command can currently execute. + /// + /// + /// The value provided by this observable is governed both by any canExecute observable provided during + /// command creation, as well as the current execution status of the command. A command that is currently executing + /// will always yield false from this observable, even if the canExecute pipeline is currently true. + /// + IObservable CanExecute { get; } + } +} diff --git a/src/ReactiveUI/ReactiveCommand/ReactiveCommand.cs b/src/ReactiveUI/ReactiveCommand/ReactiveCommand.cs index b1b764d42c..23ba5e5779 100644 --- a/src/ReactiveUI/ReactiveCommand/ReactiveCommand.cs +++ b/src/ReactiveUI/ReactiveCommand/ReactiveCommand.cs @@ -5,7 +5,6 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Linq.Expressions; using System.Reactive; using System.Reactive.Concurrency; using System.Reactive.Disposables; @@ -14,7 +13,6 @@ using System.Reactive.Threading.Tasks; using System.Threading; using System.Threading.Tasks; -using System.Windows.Input; namespace ReactiveUI { @@ -23,51 +21,10 @@ namespace ReactiveUI /// /// /// - /// This non-generic base class defines the base behavior for all reactive commands. - /// - /// - /// Reactive commands encapsulate the behavior of running some execution logic and then surfacing the results on the UI - /// thread. Importantly, no scheduling is performed against input observables (the canExecute and execution pipelines). - /// - /// - /// To create an instance of ReactiveCommand, call one of the static creation methods defined by this class. - /// can be used when your execution logic is synchronous. - /// and - /// (and overloads) can be used for asynchronous - /// execution logic. Optionally, you can provide an observable that governs the availability of the command for execution, - /// as well as a scheduler to which events will be delivered. - /// - /// - /// The property provides an observable that can be used to determine whether the command is - /// eligible for execution. The value of this observable is determined by both the canExecute observable provided - /// during command creation, and the current execution status of the command. A command that is already executing will - /// yield false from its observable regardless of the canExecute observable provided - /// during command creation. - /// - /// - /// The property provides an observable whose value indicates whether the command is currently - /// executing. This can be a useful means of triggering UI, such as displaying an activity indicator whilst a command is - /// executing. - /// - /// - /// As discussed above, you are under no obligation to somehow incorporate this into your canExecute observable - /// because that is taken care of for you. That is, if the value of IsExecuting is true, the value of - /// CanExecute will be false. However, if the value of CanExecute is false, that does not imply - /// the value of IsExecuting is true. - /// - /// - /// Any errors in your command's execution logic (including any canExecute observable you choose to provide) will be - /// surfaced via the observable. This gives you the opportunity to handle the error before - /// it triggers a default handler that tears down the application. For example, you might use this as a means of alerting - /// the user that something has gone wrong executing the command. - /// - /// - /// For the sake of convenience, all ReactiveCommand instances are also implementations of . - /// This allows you to easily integrate instances of ReactiveCommand into platforms that understands ICommand - /// natively (such as WPF and UWP). + /// This non-generic base class defines the creation behavior of the ReactiveCommand's. /// /// - public abstract partial class ReactiveCommand + public static class ReactiveCommand { /// /// Creates a parameterless with synchronous execution logic. @@ -562,107 +519,6 @@ public static CombinedReactiveCommand CreateCombined - /// Abstract base class of the ReactiveCommand's. Meant only for interop with the ICommand interface. - /// - public abstract partial class ReactiveCommand : IDisposable, ICommand, IHandleObservableErrors - { - private EventHandler _canExecuteChanged; - - /// - event EventHandler ICommand.CanExecuteChanged - { - add => _canExecuteChanged += value; - remove => _canExecuteChanged -= value; - } - - /// - /// An observable whose value indicates whether the command can currently execute. - /// - /// - /// The value provided by this observable is governed both by any canExecute observable provided during - /// command creation, as well as the current execution status of the command. A command that is currently executing - /// will always yield false from this observable, even if the canExecute pipeline is currently true. - /// - public abstract IObservable CanExecute - { - get; - } - - /// - /// An observable whose value indicates whether the command is currently executing. - /// - /// - /// This observable can be particularly useful for updating UI, such as showing an activity indicator whilst a command - /// is executing. - /// - public abstract IObservable IsExecuting - { - get; - } - - /// - /// An observable that ticks any exceptions in command execution logic. - /// - /// - /// Any exceptions that are not observed via this observable will propagate out and cause the application to be torn - /// down. Therefore, you will always want to subscribe to this observable if you expect errors could occur (e.g. if - /// your command execution includes network activity). - /// - public abstract IObservable ThrownExceptions - { - get; - } - - /// - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - /// - bool ICommand.CanExecute(object parameter) - { - return ICommandCanExecute(parameter); - } - - /// - void ICommand.Execute(object parameter) - { - ICommandExecute(parameter); - } - - /// - /// Disposes of the managed resources. - /// - /// If its getting called by the Dispose() method. - protected abstract void Dispose(bool disposing); - - /// - /// Will be called by the methods from the ICommand interface. - /// This method is called when the Command should evaluate if it can execute. - /// - /// The parameter being passed to the ICommand. - /// If the command can be executed. - protected abstract bool ICommandCanExecute(object parameter); - - /// - /// Will be called by the methods from the ICommand interface. - /// This method is called when the Command should execute. - /// - /// The parameter being passed to the ICommand. - protected abstract void ICommandExecute(object parameter); - - /// - /// Will trigger a event when the CanExecute condition has changed. - /// - protected void OnCanExecuteChanged() - { - _canExecuteChanged?.Invoke(this, EventArgs.Empty); - } - } - /// /// Encapsulates a user interaction behind a reactive interface. /// diff --git a/src/ReactiveUI/ReactiveCommand/ReactiveCommandBase.cs b/src/ReactiveUI/ReactiveCommand/ReactiveCommandBase.cs index 4c2dd0fd8c..9276bca8e9 100644 --- a/src/ReactiveUI/ReactiveCommand/ReactiveCommandBase.cs +++ b/src/ReactiveUI/ReactiveCommand/ReactiveCommandBase.cs @@ -4,6 +4,7 @@ using System; using System.Reactive.Linq; +using System.Windows.Input; namespace ReactiveUI { @@ -19,6 +20,47 @@ namespace ReactiveUI /// Because the result type is known by this class, it can implement . However, the implementation /// is defined as abstract, so subclasses must provide it. /// + /// + /// Reactive commands encapsulate the behavior of running some execution logic and then surfacing the results on the UI + /// thread. Importantly, no scheduling is performed against input observables (the canExecute and execution pipelines). + /// + /// + /// To create an instance of ReactiveCommand, call one of the static creation methods defined by this class. + /// can be used when your execution logic is synchronous. + /// and + /// (and overloads) can be used for asynchronous + /// execution logic. Optionally, you can provide an observable that governs the availability of the command for execution, + /// as well as a scheduler to which events will be delivered. + /// + /// + /// The property provides an observable that can be used to determine whether the command is + /// eligible for execution. The value of this observable is determined by both the canExecute observable provided + /// during command creation, and the current execution status of the command. A command that is already executing will + /// yield false from its observable regardless of the canExecute observable provided + /// during command creation. + /// + /// + /// The property provides an observable whose value indicates whether the command is currently + /// executing. This can be a useful means of triggering UI, such as displaying an activity indicator whilst a command is + /// executing. + /// + /// + /// As discussed above, you are under no obligation to somehow incorporate this into your canExecute observable + /// because that is taken care of for you. That is, if the value of IsExecuting is true, the value of + /// CanExecute will be false. However, if the value of CanExecute is false, that does not imply + /// the value of IsExecuting is true. + /// + /// + /// Any errors in your command's execution logic (including any canExecute observable you choose to provide) will be + /// surfaced via the observable. This gives you the opportunity to handle the error before + /// it triggers a default handler that tears down the application. For example, you might use this as a means of alerting + /// the user that something has gone wrong executing the command. + /// + /// + /// For the sake of convenience, all ReactiveCommand instances are also implementations of . + /// This allows you to easily integrate instances of ReactiveCommand into platforms that understands ICommand + /// natively (such as WPF and UWP). + /// /// /// /// The type of parameter values passed in during command execution. @@ -26,8 +68,74 @@ namespace ReactiveUI /// /// The type of the values that are the result of command execution. /// - public abstract class ReactiveCommandBase : ReactiveCommand, IObservable + public abstract class ReactiveCommandBase : IObservable, ICommand, IReactiveCommand { + private EventHandler _canExecuteChanged; + + /// + event EventHandler ICommand.CanExecuteChanged + { + add => _canExecuteChanged += value; + remove => _canExecuteChanged -= value; + } + + /// + /// An observable whose value indicates whether the command can currently execute. + /// + /// + /// The value provided by this observable is governed both by any canExecute observable provided during + /// command creation, as well as the current execution status of the command. A command that is currently executing + /// will always yield false from this observable, even if the canExecute pipeline is currently true. + /// + public abstract IObservable CanExecute + { + get; + } + + /// + /// An observable whose value indicates whether the command is currently executing. + /// + /// + /// This observable can be particularly useful for updating UI, such as showing an activity indicator whilst a command + /// is executing. + /// + public abstract IObservable IsExecuting + { + get; + } + + /// + /// An observable that ticks any exceptions in command execution logic. + /// + /// + /// Any exceptions that are not observed via this observable will propagate out and cause the application to be torn + /// down. Therefore, you will always want to subscribe to this observable if you expect errors could occur (e.g. if + /// your command execution includes network activity). + /// + public abstract IObservable ThrownExceptions + { + get; + } + + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + /// + bool ICommand.CanExecute(object parameter) + { + return ICommandCanExecute(parameter); + } + + /// + void ICommand.Execute(object parameter) + { + ICommandExecute(parameter); + } + /// /// Subscribes to execution results from this command. /// @@ -69,14 +177,37 @@ public abstract class ReactiveCommandBase : ReactiveCommand, IO /// public abstract IObservable Execute(TParam parameter = default(TParam)); - /// - protected override bool ICommandCanExecute(object parameter) + /// + /// Disposes of the managed resources. + /// + /// If its getting called by the Dispose() method. + protected abstract void Dispose(bool disposing); + + /// + /// Will trigger a event when the CanExecute condition has changed. + /// + protected void OnCanExecuteChanged() + { + _canExecuteChanged?.Invoke(this, EventArgs.Empty); + } + + /// + /// Will be called by the methods from the ICommand interface. + /// This method is called when the Command should evaluate if it can execute. + /// + /// The parameter being passed to the ICommand. + /// If the command can be executed. + protected virtual bool ICommandCanExecute(object parameter) { return CanExecute.FirstAsync().Wait(); } - /// - protected override void ICommandExecute(object parameter) + /// + /// Will be called by the methods from the ICommand interface. + /// This method is called when the Command should execute. + /// + /// The parameter being passed to the ICommand. + protected virtual void ICommandExecute(object parameter) { // ensure that null is coerced to default(TParam) so that commands taking value types will use a sensible default if no parameter is supplied if (parameter == null) From 7d82aabd814ec87e057d472f9d71c3f609a05a82 Mon Sep 17 00:00:00 2001 From: Glenn Watson Date: Wed, 7 Nov 2018 10:23:56 -0800 Subject: [PATCH 2/4] Made the IReactiveCommand public --- .../API/ApiApprovalTests.ReactiveUI.approved.txt | 7 ++++++- src/ReactiveUI/ReactiveCommand/IReactiveCommand.cs | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/ReactiveUI.Tests/API/ApiApprovalTests.ReactiveUI.approved.txt b/src/ReactiveUI.Tests/API/ApiApprovalTests.ReactiveUI.approved.txt index 54ae8b01fe..29ef2ee690 100644 --- a/src/ReactiveUI.Tests/API/ApiApprovalTests.ReactiveUI.approved.txt +++ b/src/ReactiveUI.Tests/API/ApiApprovalTests.ReactiveUI.approved.txt @@ -259,6 +259,11 @@ namespace ReactiveUI TViewModel ViewModel { get; } System.Linq.Expressions.Expression ViewModelExpression { get; } } + public interface IReactiveCommand : ReactiveUI.IHandleObservableErrors, System.IDisposable + { + System.IObservable CanExecute { get; } + System.IObservable IsExecuting { get; } + } public interface IReactiveNotifyPropertyChanged { System.IObservable> Changed { get; } @@ -486,7 +491,7 @@ namespace ReactiveUI public override System.IObservable Execute(TParam parameter = null) { } public override System.IDisposable Subscribe(System.IObserver observer) { } } - public abstract class ReactiveCommandBase : ReactiveUI.IHandleObservableErrors, System.IDisposable, System.IObservable, System.Windows.Input.ICommand + public abstract class ReactiveCommandBase : ReactiveUI.IHandleObservableErrors, ReactiveUI.IReactiveCommand, System.IDisposable, System.IObservable, System.Windows.Input.ICommand { protected ReactiveCommandBase() { } public abstract System.IObservable CanExecute { get; } diff --git a/src/ReactiveUI/ReactiveCommand/IReactiveCommand.cs b/src/ReactiveUI/ReactiveCommand/IReactiveCommand.cs index 844b8f9c31..ecf49e2de5 100644 --- a/src/ReactiveUI/ReactiveCommand/IReactiveCommand.cs +++ b/src/ReactiveUI/ReactiveCommand/IReactiveCommand.cs @@ -9,7 +9,7 @@ namespace ReactiveUI /// /// Encapsulates a user action behind a reactive interface. /// - internal interface IReactiveCommand : IDisposable, IHandleObservableErrors + public interface IReactiveCommand : IDisposable, IHandleObservableErrors { /// /// Gets an observable whose value indicates whether the command is currently executing. From 979cfea01c5ea30a5b634ef5b974b93f0dd724a5 Mon Sep 17 00:00:00 2001 From: Glenn Watson Date: Wed, 7 Nov 2018 10:32:14 -0800 Subject: [PATCH 3/4] Made the IReactiveCommand internal again. Because it doesn't derive off ICommand it can be an issue for binding etc --- .../API/ApiApprovalTests.ReactiveUI.approved.txt | 7 +------ src/ReactiveUI/ReactiveCommand/IReactiveCommand.cs | 5 ++++- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/ReactiveUI.Tests/API/ApiApprovalTests.ReactiveUI.approved.txt b/src/ReactiveUI.Tests/API/ApiApprovalTests.ReactiveUI.approved.txt index 29ef2ee690..54ae8b01fe 100644 --- a/src/ReactiveUI.Tests/API/ApiApprovalTests.ReactiveUI.approved.txt +++ b/src/ReactiveUI.Tests/API/ApiApprovalTests.ReactiveUI.approved.txt @@ -259,11 +259,6 @@ namespace ReactiveUI TViewModel ViewModel { get; } System.Linq.Expressions.Expression ViewModelExpression { get; } } - public interface IReactiveCommand : ReactiveUI.IHandleObservableErrors, System.IDisposable - { - System.IObservable CanExecute { get; } - System.IObservable IsExecuting { get; } - } public interface IReactiveNotifyPropertyChanged { System.IObservable> Changed { get; } @@ -491,7 +486,7 @@ namespace ReactiveUI public override System.IObservable Execute(TParam parameter = null) { } public override System.IDisposable Subscribe(System.IObserver observer) { } } - public abstract class ReactiveCommandBase : ReactiveUI.IHandleObservableErrors, ReactiveUI.IReactiveCommand, System.IDisposable, System.IObservable, System.Windows.Input.ICommand + public abstract class ReactiveCommandBase : ReactiveUI.IHandleObservableErrors, System.IDisposable, System.IObservable, System.Windows.Input.ICommand { protected ReactiveCommandBase() { } public abstract System.IObservable CanExecute { get; } diff --git a/src/ReactiveUI/ReactiveCommand/IReactiveCommand.cs b/src/ReactiveUI/ReactiveCommand/IReactiveCommand.cs index ecf49e2de5..b211c4978e 100644 --- a/src/ReactiveUI/ReactiveCommand/IReactiveCommand.cs +++ b/src/ReactiveUI/ReactiveCommand/IReactiveCommand.cs @@ -8,8 +8,11 @@ namespace ReactiveUI { /// /// Encapsulates a user action behind a reactive interface. + /// This is for interop inside for the command binding. + /// Not meant for external use due to the fact it doesn't implement ICommand + /// to force the user to favor the Reactive style command execution. /// - public interface IReactiveCommand : IDisposable, IHandleObservableErrors + internal interface IReactiveCommand : IDisposable, IHandleObservableErrors { /// /// Gets an observable whose value indicates whether the command is currently executing. From d6919156be043207b24937a5b522657e62f79fb2 Mon Sep 17 00:00:00 2001 From: Glenn Watson Date: Wed, 7 Nov 2018 15:36:02 -0800 Subject: [PATCH 4/4] Bump version number to 9.2 --- version.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.json b/version.json index d9ea9db23f..8ca742c336 100644 --- a/version.json +++ b/version.json @@ -1,5 +1,5 @@ { - "version": "9.1", + "version": "9.2", "publicReleaseRefSpec": [ "^refs/heads/master$", // we release out of master "^refs/heads/develop$", // we release out of develop