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