forked from PeteGoo/ReactiveUI
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathReactiveCommand.cs
More file actions
160 lines (140 loc) · 5.87 KB
/
ReactiveCommand.cs
File metadata and controls
160 lines (140 loc) · 5.87 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reactive.Concurrency;
using System.Reactive.Linq;
using System.Reactive.Subjects;
using System.Windows.Input;
using ReactiveUI;
namespace ReactiveUI.Xaml
{
/// <summary>
/// IReactiveCommand is an Rx-enabled version of ICommand that is also an
/// Observable. Its Observable fires once for each invocation of
/// ICommand.Execute and its value is the CommandParameter that was
/// provided.
/// </summary>
public class ReactiveCommand : IReactiveCommand, IEnableLogger, IDisposable
{
/// <summary>
/// Creates a new ReactiveCommand object.
/// </summary>
/// <param name="canExecute">An Observable, often obtained via
/// ObservableFromProperty, that defines when the Command can
/// execute.</param>
/// <param name="scheduler">The scheduler to publish events on - default
/// is RxApp.DeferredScheduler.</param>
public ReactiveCommand(IObservable<bool> canExecute = null, IScheduler scheduler = null)
{
canExecute = canExecute ?? Observable.Return(true).Concat(Observable.Never<bool>());
commonCtor(scheduler);
_inner = canExecute.Subscribe(canExecuteSubject.OnNext);
}
protected ReactiveCommand(Func<object, bool> canExecute, IScheduler scheduler = null)
{
canExecuteExplicitFunc = canExecute;
commonCtor(scheduler);
}
/// <summary>
/// Creates a new ReactiveCommand object in an imperative, non-Rx way,
/// similar to RelayCommand.
/// </summary>
/// <param name="canExecute">A function that determines when the Command
/// can execute.</param>
/// <param name="executed">A method that will be invoked when the
/// Execute method is invoked.</param>
/// <param name="scheduler">The scheduler to publish events on - default
/// is RxApp.DeferredScheduler.</param>
/// <returns>A new ReactiveCommand object.</returns>
public static ReactiveCommand Create(
Func<object, bool> canExecute,
Action<object> executed = null,
IScheduler scheduler = null)
{
var ret = new ReactiveCommand(canExecute, scheduler);
if (executed != null) {
ret.Subscribe(executed);
}
return ret;
}
void commonCtor(IScheduler scheduler)
{
this.scheduler = scheduler ?? RxApp.DeferredScheduler;
canExecuteSubject = new ScheduledSubject<bool>(RxApp.DeferredScheduler);
canExecuteLatest = new ObservableAsPropertyHelper<bool>(canExecuteSubject,
b => { if (CanExecuteChanged != null) CanExecuteChanged(this, EventArgs.Empty); },
true, scheduler);
executeSubject = new Subject<object>();
}
Func<object, bool> canExecuteExplicitFunc;
protected ISubject<bool> canExecuteSubject;
IDisposable _inner = null;
/// <summary>
/// Fires whenever the CanExecute of the ICommand changes.
/// </summary>
public IObservable<bool> CanExecuteObservable {
get { return canExecuteSubject.DistinctUntilChanged(); }
}
ObservableAsPropertyHelper<bool> canExecuteLatest;
public virtual bool CanExecute(object parameter)
{
if (canExecuteExplicitFunc != null) {
bool ret = canExecuteExplicitFunc(parameter);
canExecuteSubject.OnNext(ret);
return ret;
}
return canExecuteLatest.Value;
}
public event EventHandler CanExecuteChanged;
IScheduler scheduler;
Subject<object> executeSubject;
public void Execute(object parameter)
{
this.Log().InfoFormat("{0:X}: Executed", this.GetHashCode());
executeSubject.OnNext(parameter);
}
public IDisposable Subscribe(IObserver<object> observer)
{
return executeSubject.ObserveOn(scheduler).Subscribe(observer);
}
public void Dispose()
{
if (_inner != null) {
_inner.Dispose();
}
}
}
public static class ReactiveCommandMixins
{
/// <summary>
/// ToCommand is a convenience method for returning a new
/// ReactiveCommand based on an existing Observable chain.
/// </summary>
/// <param name="scheduler">The scheduler to publish events on - default
/// is RxApp.DeferredScheduler.</param>
/// <returns>A new ReactiveCommand whose CanExecute Observable is the
/// current object.</returns>
public static ReactiveCommand ToCommand(this IObservable<bool> This, IScheduler scheduler = null)
{
return new ReactiveCommand(This, scheduler);
}
/// <summary>
/// A utility method that will pipe an Observable to an ICommand (i.e.
/// it will first call its CanExecute with the provided value, then if
/// the command can be executed, Execute() will be called)
/// </summary>
/// <param name="command">The command to be executed.</param>
/// <returns>An object that when disposes, disconnects the Observable
/// from the command.</returns>
public static IDisposable InvokeCommand<T>(this IObservable<T> This, ICommand command)
{
return This.ObserveOn(RxApp.DeferredScheduler).Subscribe(x => {
if (!command.CanExecute(x)) {
return;
}
command.Execute(x);
});
}
}
}
// vim: tw=120 ts=4 sw=4 et :