PageRenderTime 40ms CodeModel.GetById 16ms app.highlight 18ms RepoModel.GetById 2ms app.codeStats 0ms

/MVVMSidekick/MVVMSidekick.Shared/Reactive.cs

https://github.com/zoujuny/MVVM-Sidekick
C# | 406 lines | 294 code | 73 blank | 39 comment | 3 complexity | ba476da1c018e1a1a9f49cdd7720a374 MD5 | raw file
  1using System;
  2using System.Collections.Generic;
  3using System.Linq;
  4using System.Text;
  5using System.ComponentModel;
  6using System.Linq.Expressions;
  7using System.Runtime.Serialization;
  8using System.Reflection;
  9using System.Threading.Tasks;
 10using System.Threading;
 11using System.Windows.Input;
 12using MVVMSidekick.ViewModels;
 13using MVVMSidekick.Commands;
 14using System.Runtime.CompilerServices;
 15using MVVMSidekick.Reactive;
 16using System.Reactive.Linq;
 17using System.Reactive.Subjects;
 18using System.Reactive;
 19using System.Reactive.Threading.Tasks;
 20using MVVMSidekick.EventRouting;
 21using System.Collections.ObjectModel;
 22using System.Collections.Specialized;
 23using System.IO;
 24using System.Collections;
 25using MVVMSidekick.Utilities;
 26#if NETFX_CORE
 27using Windows.UI.Xaml;
 28using Windows.UI.Xaml.Data;
 29using Windows.UI.Xaml.Controls;
 30using System.Collections.Concurrent;
 31using Windows.UI.Xaml.Navigation;
 32
 33using Windows.UI.Xaml.Controls.Primitives;
 34
 35#elif WPF
 36using System.Windows;
 37using System.Windows.Controls;
 38using System.Windows.Data;
 39using System.Collections.Concurrent;
 40using System.Windows.Navigation;
 41
 42using MVVMSidekick.Views;
 43using System.Windows.Controls.Primitives;
 44
 45
 46#elif SILVERLIGHT_5||SILVERLIGHT_4
 47using System.Windows;
 48using System.Windows.Controls;
 49using System.Windows.Data;
 50using System.Windows.Navigation;
 51using System.Windows.Controls.Primitives;
 52#elif WINDOWS_PHONE_8||WINDOWS_PHONE_7
 53using System.Windows;
 54using System.Windows.Controls;
 55using Microsoft.Phone.Controls;
 56using System.Windows.Data;
 57using System.Windows.Navigation;
 58using System.Windows.Controls.Primitives;
 59#endif
 60
 61
 62namespace MVVMSidekick
 63{
 64    namespace Reactive
 65    {
 66
 67        public static class EventTuple
 68        {
 69            public static EventTuple<TSource, TEventArgs> Create<TSource, TEventArgs>(TSource source, TEventArgs eventArgs)
 70            {
 71                return new EventTuple<TSource, TEventArgs> { Source = source, EventArgs = eventArgs };
 72            }
 73
 74        }
 75        public struct EventTuple<TSource, TEventArgs>
 76        {
 77            public TSource Source { get; set; }
 78            public TEventArgs EventArgs { get; set; }
 79        }
 80
 81        public static class MVVMRxExtensions
 82        {
 83
 84
 85            /// <summary>
 86            /// Register a Do action to the observer, Notify the value in this sequence to EventRouter
 87            /// </summary>
 88            /// <typeparam name="T">Sequence Value Type</typeparam>
 89            /// <param name="sequence">value sequence</param>
 90            /// <param name="eventRounter"> target </param>
 91            /// <param name="source">value source</param>
 92            /// <param name="registerName">log name</param>
 93            /// <returns>same value sequence inputed</returns>
 94            public static IObservable<T> DoNotifyEventRouter<T>(this IObservable<T> sequence, EventRouter eventRounter, object source = null, [CallerMemberName] string registerName = null)
 95            {
 96                return
 97                    sequence.Do(
 98                            v => eventRounter.RaiseEvent(source, v, registerName)
 99
100                        );
101
102            }
103
104            /// <summary>
105            /// Register a Do action to the observer, Notify the value in this sequence to EventRouter
106            /// </summary>
107            /// <typeparam name="T">Sequence Value Type</typeparam>
108            /// <param name="sequence">value sequence</param>
109            /// <param name="source">value source</param>
110            /// <param name="registerName">log name</param>
111            /// <returns>same value sequence inputed</returns>
112            public static IObservable<T> DoNotifyDefaultEventRouter<T>(this IObservable<T> sequence, object source = null, [CallerMemberName] string registerName = null)
113            {
114                return DoNotifyEventRouter(sequence, EventRouter.Instance, source, registerName);
115            }
116
117
118
119            public static IObservable<Task<Tout>> DoExecuteUIBusyTask<Tin, Tout>(this IObservable<Tin> sequence, IViewModel vm, Func<Tin, CancellationToken, Task<Tout>> taskBody, CancellationToken cancellationToken)
120            {
121                return sequence.Select
122                    (
123                        inContext => vm.ExecuteTask(taskBody, inContext, cancellationToken, true)
124                    );
125            }
126
127            public static IObservable<Task<Tout>> DoExecuteUITask<Tin, Tout>(this IObservable<Tin> sequence, IViewModel vm, Func<Tin, CancellationToken, Task<Tout>> taskBody, CancellationToken cancellationToken)
128            {
129                return sequence.Select
130                    (
131                        inContext => vm.ExecuteTask(taskBody, inContext, cancellationToken, false)
132                    );
133            }
134            public static IObservable<Task> DoExecuteUIBusyTask<Tin>(this IObservable<Tin> sequence, IViewModel vm, Func<Tin, CancellationToken, Task> taskBody, CancellationToken cancellationToken)
135            {
136                return sequence.Select
137                (
138                    inContext => vm.ExecuteTask(taskBody, inContext, cancellationToken, true)
139                );
140            }
141
142            public static IObservable<Task> DoExecuteUITask<Tin>(this IObservable<Tin> sequence, IViewModel vm, Func<Tin, CancellationToken, Task> taskBody, CancellationToken cancellationToken)
143            {
144                return sequence.Select
145               (
146                   inContext => vm.ExecuteTask(taskBody, inContext, cancellationToken, false)
147               );
148            }
149
150
151
152
153
154            public static IObservable<Task<Tout>> DoExecuteUIBusyTask<Tin, Tout>(this IObservable<Tin> sequence, IViewModel vm, Func<Tin, Task<Tout>> taskBody)
155            {
156                return sequence.Select
157                    (
158                        inContext => vm.ExecuteTask(taskBody, inContext, true)
159                    );
160            }
161
162            public static IObservable<Task<Tout>> DoExecuteUITask<Tin, Tout>(this IObservable<Tin> sequence, IViewModel vm, Func<Tin, Task<Tout>> taskBody)
163            {
164                return sequence.Select
165                    (
166                        inContext => vm.ExecuteTask(taskBody, inContext, false)
167                    );
168            }
169            public static IObservable<Task> DoExecuteUIBusyTask<Tin>(this IObservable<Tin> sequence, IViewModel vm, Func<Tin, Task> taskBody)
170            {
171                return sequence.Select
172                (
173                    inContext => vm.ExecuteTask(taskBody, inContext, true)
174                );
175            }
176
177            public static IObservable<Task> DoExecuteUITask<Tin>(this IObservable<Tin> sequence, IViewModel vm, Func<Tin, Task> taskBody)
178            {
179                return sequence.Select
180               (
181                   inContext => vm.ExecuteTask(taskBody, inContext, false)
182               );
183            }
184
185            /// <summary>
186            /// <para>Create a instance of IObservable that fires when property changed event is raised.</para>
187            /// <para>创建一个监视属性变化事件观察者IObservable实例。</para>
188            /// </summary>
189            /// <returns></returns>
190            public static IObservable<EventPattern<PropertyChangedEventArgs>> CreatePropertyChangedObservable(this BindableBase bindable)
191            {
192                return Observable.FromEventPattern<PropertyChangedEventHandler, PropertyChangedEventArgs>(
193                        eh => bindable.PropertyChanged += eh,
194                        eh => bindable.PropertyChanged -= eh
195                    )
196                    .Where(_ => bindable.IsNotificationActivated);
197            }
198
199
200            public static IObservable<EventPattern<NotifyCollectionChangedEventArgs>> GetEventObservable<T>(this ObservableCollection<T> source, BindableBase model)
201            {
202                var rval = Observable
203                  .FromEventPattern<NotifyCollectionChangedEventHandler, NotifyCollectionChangedEventArgs>
204                      (
205                          ev => source.CollectionChanged += ev,
206                          ev => source.CollectionChanged -= ev
207                      ).Where(_ => model.IsNotificationActivated);
208                return rval;
209            }
210            public static IObservable<EventTuple<ValueContainer<TValue>, TValue>> GetNewValueObservable<TValue>
211                (
212                    this ValueContainer<TValue> source
213
214                )
215            {
216
217                return Observable.FromEventPattern<EventHandler<ValueChangedEventArgs<TValue>>, ValueChangedEventArgs<TValue>>(
218                        eh => source.ValueChanged += eh,
219                        eh => source.ValueChanged -= eh)
220                        .Select(
221                            x => EventTuple.Create(source, x.EventArgs.NewValue)
222
223                        );
224
225            }
226
227            public static IObservable<EventTuple<ValueContainer<TValue>, ValueChangedEventArgs<TValue>>>
228                GetEventObservable<TValue>(this ValueContainer<TValue> source)
229            {
230
231                var eventArgSeq = Observable.FromEventPattern<EventHandler<ValueChangedEventArgs<TValue>>, ValueChangedEventArgs<TValue>>(
232                        eh => source.ValueChanged += eh,
233                        eh => source.ValueChanged -= eh);
234                return eventArgSeq.Select(
235                            x => EventTuple.Create(source, x.EventArgs)
236                        );
237                ;
238            }
239
240
241            public static IObserver<TValue> AsObserver<TValue>(this ValueContainer<TValue> source)
242            {
243                return Observer.Create<TValue>(v => source.SetValueAndTryNotify(v));
244
245            }
246
247
248            public static IObservable<object> GetNullObservable<TValue>(this ValueContainer<TValue> source)
249            {
250
251                var eventArgSeq = Observable.FromEventPattern<EventHandler<ValueChangedEventArgs<TValue>>, ValueChangedEventArgs<TValue>>(
252                        eh => source.ValueChanged += eh,
253                        eh => source.ValueChanged -= eh);
254                return eventArgSeq.Select(
255                            x => null as object
256                        );
257                ;
258            }
259            /// <summary>
260            /// 转化
261            /// </summary>
262            /// <typeparam name="TEventArgs"></typeparam>
263            /// <param name="source"></param>
264            /// <returns></returns>
265            [Obsolete("The source is already  IObservable<RouterEventData<TEventArgs>>")]
266            public static IObservable<RouterEventData<TEventArgs>>
267                GetRouterEventObservable<TEventArgs>(this MVVMSidekick.EventRouting.EventRouter.EventObject<TEventArgs> source)
268#if !NETFX_CORE
269 where TEventArgs : EventArgs
270#endif
271            {
272
273
274                return source;
275
276            }
277            /// <summary>
278            /// Bind Command to IsUIBusy property.
279            /// </summary>
280            /// <typeparam name="TCommand">A sub class of ReactiveCommand</typeparam>
281            /// <typeparam name="TResource">The resource type of CommandModel</typeparam>
282            /// <typeparam name="TViewModel">The View Model type command wanna bind to</typeparam>
283            /// <param name="command">Command itself</param>
284            /// <param name="model">The View Model  command wanna bind to</param>
285            /// <param name="canExecuteWhenBusy">if can execute when ui busy , input true</param>
286            /// <returns>command instance itself</returns>
287            public static CommandModel<TCommand, TResource> ListenToIsUIBusy<TCommand, TResource, TViewModel>(this CommandModel<TCommand, TResource> command, ViewModelBase<TViewModel> model, bool canExecuteWhenBusy = false)
288                where TViewModel : ViewModelBase<TViewModel>
289                where TCommand : ReactiveCommand
290            {
291
292                //See Test  CommandListenToUIBusy_Test
293                Observable.Range(0, 1)
294                    .Select(x => (x == 0) ? !command.LastCanExecuteValue : command.LastCanExecuteValue)
295                    .Concat(
296                        model.GetValueContainer(x => x.IsUIBusy)
297                        .GetNewValueObservable()
298                        .Select(e =>
299                             canExecuteWhenBusy ? canExecuteWhenBusy : (!e.EventArgs)
300                        ))
301                    .Subscribe(command.CommandCore.CanExecuteObserver)
302                    .DisposeWith(model);
303
304                return command;
305            }
306
307        }
308
309        public class ReactiveCommand : EventCommandBase, ICommand, IObservable<EventPattern<EventCommandEventArgs>>
310        {
311
312
313
314            protected Lazy<IObservable<EventPattern<EventCommandEventArgs>>> _LazyObservableExecute;
315            protected Lazy<IObserver<bool>> _LazyObserverCanExecute;
316            protected bool _CurrentCanExecuteObserverValue;
317
318            protected ReactiveCommand()
319            {
320                ConfigReactive();
321
322            }
323
324            public ReactiveCommand(bool canExecute = false)
325                : this()
326            {
327                _CurrentCanExecuteObserverValue = canExecute;
328            }
329
330
331             protected void ConfigReactive()
332            {
333                _LazyObservableExecute = new Lazy<IObservable<EventPattern<EventCommandEventArgs>>>
334                (
335                    () =>
336                    {
337                        var ob = Observable.FromEventPattern<EventHandler<EventCommandEventArgs>, EventCommandEventArgs>
338                    (
339                        eh =>
340                        {
341                            this.CommandExecute += eh;
342                        },
343                        eh =>
344                        {
345                            this.CommandExecute -= eh;
346                        }
347                    );
348
349                        return ob;
350                    }
351                );
352
353                _LazyObserverCanExecute = new Lazy<IObserver<bool>>
354                (
355                    () =>
356                        Observer.Create<bool>(
357                        canExe =>
358                        {
359                            var oldv = this._CurrentCanExecuteObserverValue;
360                            _CurrentCanExecuteObserverValue = canExe;
361                            if (oldv != canExe)
362                            {
363                                OnCanExecuteChanged();
364                            }
365                        }
366                        )
367
368                );
369            }
370            public IObserver<bool> CanExecuteObserver { get { return _LazyObserverCanExecute.Value; } }
371
372            public override bool CanExecute(object parameter)
373            {
374                return _CurrentCanExecuteObserverValue;
375            }
376
377
378
379
380
381
382            public IDisposable Subscribe(IObserver<EventPattern<EventCommandEventArgs>> observer)
383            {
384                return _LazyObservableExecute
385                      .Value
386                      .Subscribe(observer);
387            }
388        }
389
390
391        public class TaskExecutionWindowEventArg : EventArgs
392        {
393
394            public TaskExecutionWindowEventArg(Task executedTask, CallingCodeContext callingContext)
395            {
396                TaskWindow = executedTask.ToObservable();
397                CallingCodeContext = callingContext;
398            }
399
400            public IObservable<Unit> TaskWindow { get; private set; }
401            public CallingCodeContext CallingCodeContext { get; private set; }
402
403        }
404
405    }
406}