PageRenderTime 44ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/MVVMSidekick/MVVMSidekick.Shared/Utilities.cs

https://github.com/zoujuny/MVVM-Sidekick
C# | 696 lines | 468 code | 142 blank | 86 comment | 24 complexity | d77495bc613bbda7b4ff202dd9d3e63e 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 MVVMSidekick.EventRouting;
  20. using System.Collections.ObjectModel;
  21. using System.Collections.Specialized;
  22. using System.IO;
  23. using System.Collections;
  24. using System.Security;
  25. #if NETFX_CORE
  26. using Windows.UI.Xaml;
  27. using Windows.UI.Xaml.Data;
  28. using Windows.UI.Xaml.Controls;
  29. using System.Collections.Concurrent;
  30. using Windows.UI.Xaml.Navigation;
  31. using Windows.UI.Xaml.Controls.Primitives;
  32. using Windows.System.Threading;
  33. using System.Reactive.Disposables;
  34. #elif WPF
  35. using System.Windows;
  36. using System.Windows.Controls;
  37. using System.Windows.Data;
  38. using System.Collections.Concurrent;
  39. using System.Windows.Navigation;
  40. using MVVMSidekick.Views;
  41. using System.Windows.Controls.Primitives;
  42. using MVVMSidekick.Services;
  43. using System.Reactive.Disposables;
  44. #elif SILVERLIGHT_5||SILVERLIGHT_4
  45. using System.Windows;
  46. using System.Windows.Controls;
  47. using System.Windows.Data;
  48. using System.Windows.Navigation;
  49. using System.Windows.Controls.Primitives;
  50. using System.Reactive.Disposables;
  51. #elif WINDOWS_PHONE_8||WINDOWS_PHONE_7
  52. using System.Windows;
  53. using System.Windows.Controls;
  54. using Microsoft.Phone.Controls;
  55. using System.Windows.Data;
  56. using System.Windows.Navigation;
  57. using System.Windows.Controls.Primitives;
  58. using Microsoft.Phone.Reactive;
  59. #endif
  60. namespace MVVMSidekick
  61. {
  62. namespace Utilities
  63. {
  64. public static class Runtime
  65. {
  66. static bool? _IsInDesignMode;
  67. /// <summary>
  68. /// <para>Gets if the code is running in design time. </para>
  69. /// <para>读取目前是否在设计时状态。</para>
  70. /// </summary>
  71. public static bool IsInDesignMode
  72. {
  73. get
  74. {
  75. return (
  76. _IsInDesignMode
  77. ??
  78. (
  79. _IsInDesignMode =
  80. #if SILVERLIGHT_5||WINDOWS_PHONE_8||WINDOWS_PHONE_7
  81. DesignerProperties.IsInDesignTool
  82. #elif NETFX_CORE
  83. Windows.ApplicationModel.DesignMode.DesignModeEnabled
  84. #else
  85. (bool)System.ComponentModel.DependencyPropertyDescriptor
  86. .FromProperty(
  87. DesignerProperties.IsInDesignModeProperty,
  88. typeof(System.Windows.FrameworkElement))
  89. .Metadata
  90. .DefaultValue
  91. #endif
  92. ))
  93. .Value;
  94. }
  95. }
  96. }
  97. /// <summary>
  98. /// 代码调用上下文
  99. /// Calling code-context
  100. /// </summary>
  101. public struct CallingCodeContext
  102. {
  103. /// <summary>
  104. /// 创建一个当前调用上下文数据
  105. /// </summary>
  106. /// <param name="comment">注释</param>
  107. /// <param name="caller">调用者</param>
  108. /// <param name="file">文件</param>
  109. /// <param name="line">行数</param>
  110. public CallingCodeContext(bool autoFillProperties, string comment = "", [CallerMemberName] string caller = "", [CallerFilePath] string file = "", [CallerLineNumber]int line = -1)
  111. : this()
  112. {
  113. if (autoFillProperties)
  114. {
  115. Caller = caller;
  116. Comment = comment;
  117. File = file;
  118. Line = line;
  119. }
  120. }
  121. /// <summary>
  122. /// 创建一个当前调用上下文数据
  123. /// </summary>
  124. /// <param name="comment">注释</param>
  125. /// <param name="caller">调用者</param>
  126. /// <param name="file">文件</param>
  127. /// <param name="line">行数</param>
  128. /// <returns>数据</returns>
  129. public static CallingCodeContext Create(string comment = "", [CallerMemberName] string caller = "", [CallerFilePath] string file = "", [CallerLineNumber]int line = -1)
  130. {
  131. return new CallingCodeContext
  132. (true, comment, caller, file, line);
  133. }
  134. /// <summary>
  135. /// <para>Comment of this Calling .</para>
  136. /// <para>对此次Calling 的附加说明</para>
  137. /// </summary>
  138. public string Comment { get; private set; }
  139. /// <summary>
  140. /// <para>Caller Member Name of this Calling registeration.</para>
  141. /// <para>此次Calling 注册的来源</para>
  142. /// </summary>
  143. public string Caller { get; private set; }
  144. /// <summary>
  145. /// <para>Code file path of this Calling registeration.</para>
  146. /// <para>注册此次Calling 注册的代码文件</para>
  147. /// </summary>
  148. public string File { get; private set; }
  149. /// <summary>
  150. /// <para>Code line number of this Calling registeration.</para>
  151. /// <para>注册此次Calling 注册的代码行</para>
  152. /// </summary>
  153. public int Line { get; private set; }
  154. }
  155. /// <summary>
  156. /// Unify Task(4.5) and TaskEx (SL5) method in this helper
  157. /// </summary>
  158. public static class TaskExHelper
  159. {
  160. public static async Task Yield()
  161. {
  162. #if SILVERLIGHT_5||WINDOWS_PHONE_7||NET40
  163. await TaskEx.Yield();
  164. #else
  165. await Task.Yield();
  166. #endif
  167. }
  168. public static async Task<T> FromResult<T>(T result)
  169. {
  170. #if SILVERLIGHT_5||WINDOWS_PHONE_7||NET40
  171. return await TaskEx.FromResult(result);
  172. #else
  173. return await Task.FromResult(result);
  174. #endif
  175. }
  176. public static async Task Delay(int ms)
  177. {
  178. #if SILVERLIGHT_5||WINDOWS_PHONE_7||NET40
  179. await TaskEx.Delay(ms);
  180. #else
  181. await Task.Delay(ms);
  182. #endif
  183. }
  184. }
  185. /// <summary>
  186. /// Unify Type(4.5 SL & WP) and TypeInfo (Windows Runtime) class in this helper
  187. /// </summary>
  188. public static class TypeInfoHelper
  189. {
  190. #if NETFX_CORE
  191. public static TypeInfo GetTypeOrTypeInfo(this Type type)
  192. {
  193. return type.GetTypeInfo();
  194. }
  195. #else
  196. public static Type GetTypeOrTypeInfo(this Type type)
  197. {
  198. return type;
  199. }
  200. #endif
  201. }
  202. public static class ReflectionCache
  203. {
  204. static class ReflectInfoCache<T> where T : MemberInfo
  205. {
  206. static ConcurrentDictionary<Type, Dictionary<string, T>> cache
  207. = new ConcurrentDictionary<Type, Dictionary<string, T>>();
  208. static public Dictionary<string, T> GetCache(Type type, Func<Type, T[]> dataGetter)
  209. {
  210. return cache.GetOrAdd(type, s => dataGetter(s).ToDictionary(x => x.Name, x => x));
  211. }
  212. }
  213. public static Dictionary<string, MethodInfo> GetMethodsFromCache(this Type type)
  214. {
  215. #if NETFX_CORE
  216. return ReflectInfoCache<MethodInfo>.GetCache(type, x => x.GetRuntimeMethods().ToArray());
  217. #else
  218. return ReflectInfoCache<MethodInfo>.GetCache(type, x => x.GetMethods());
  219. #endif
  220. }
  221. public static Dictionary<string, EventInfo> GetEventsFromCache(this Type type)
  222. {
  223. #if NETFX_CORE
  224. return ReflectInfoCache<EventInfo>.GetCache(type, x => x.GetRuntimeEvents().ToArray());
  225. #else
  226. return ReflectInfoCache<EventInfo>.GetCache(type, x => x.GetEvents());
  227. #endif
  228. }
  229. }
  230. public delegate void EventHandlerInvoker(object sender, object eventArgs, string eventName, Type eventHandlerType);
  231. public static class EventHandlerHelper
  232. {
  233. private static Delegate CreateHandler(
  234. Expression<EventHandlerInvoker> bind,
  235. string eventName,
  236. Type delegateType,
  237. Type[] eventParametersTypes
  238. )
  239. {
  240. var pars =
  241. eventParametersTypes
  242. .Select(
  243. et => System.Linq.Expressions.Expression.Parameter(et))
  244. .ToArray();
  245. var en = System.Linq.Expressions.Expression.Constant(eventName, typeof(string));
  246. var eht = System.Linq.Expressions.Expression.Constant(delegateType, typeof(Type));
  247. var expInvoke = System.Linq.Expressions.Expression.Invoke(bind, pars[0], pars[1], en, eht);
  248. var lambda = System.Linq.Expressions.Expression.Lambda(delegateType, expInvoke, pars);
  249. var compiled = lambda.Compile();
  250. return compiled;
  251. }
  252. public static IDisposable BindEvent(this object sender, string eventName, EventHandlerInvoker executeAction)
  253. {
  254. var t = sender.GetType();
  255. while (t != null)
  256. {
  257. var es = t.GetEventsFromCache();
  258. EventInfo ei = es.MatchOrDefault(eventName);
  259. if (ei != null)
  260. {
  261. var handlerType = ei.EventHandlerType;
  262. var eventMethod = handlerType.GetMethodsFromCache().MatchOrDefault("Invoke");
  263. if (eventMethod != null)
  264. {
  265. var pts = eventMethod.GetParameters().Select(p => p.ParameterType)
  266. .ToArray();
  267. var newHandler = CreateHandler(
  268. (o, e, en, ehtype) => executeAction(o, e, en, ehtype),
  269. eventName,
  270. handlerType,
  271. pts
  272. );
  273. #if NETFX_CORE ||WINDOWS_PHONE_8
  274. var etmodule = sender.GetType().GetTypeOrTypeInfo().Module;
  275. try
  276. {
  277. return DoNetEventBind(sender, ei, newHandler);
  278. }
  279. catch (InvalidOperationException ex)
  280. {
  281. var newMI = WinRTEventBindMethodInfo.MakeGenericMethod(newHandler.GetType());
  282. var rval = newMI.Invoke(null, new object[] { sender, ei, newHandler }) as IDisposable;
  283. return rval;
  284. }
  285. #else
  286. return DoNetEventBind(sender, ei, newHandler);
  287. #endif
  288. }
  289. return null;
  290. }
  291. t = t.GetTypeOrTypeInfo().BaseType;
  292. }
  293. return null;
  294. }
  295. #if NETFX_CORE||WINDOWS_PHONE_8
  296. static MethodInfo WinRTEventBindMethodInfo = typeof(EventHandlerHelper).GetTypeInfo().GetDeclaredMethod("WinRTEventBind");
  297. private static IDisposable WinRTEventBind<THandler>(object sender, EventInfo ei, object handler)
  298. {
  299. System.Runtime.InteropServices.WindowsRuntime.EventRegistrationToken tk = default(System.Runtime.InteropServices.WindowsRuntime.EventRegistrationToken);
  300. Action<System.Runtime.InteropServices.WindowsRuntime.EventRegistrationToken> remove
  301. = et =>
  302. {
  303. ei.RemoveMethod.Invoke(sender, new object[] { et });
  304. };
  305. System.Runtime.InteropServices.WindowsRuntime.WindowsRuntimeMarshal.AddEventHandler<THandler>(
  306. ev =>
  307. {
  308. tk = (System.Runtime.InteropServices.WindowsRuntime.EventRegistrationToken)ei.AddMethod.Invoke(sender, new object[] { ev });
  309. return tk;
  310. },
  311. remove,
  312. (THandler)handler);
  313. return Disposable.Create(() =>
  314. {
  315. System.Runtime.InteropServices.WindowsRuntime.WindowsRuntimeMarshal.RemoveEventHandler<THandler>(
  316. remove,
  317. (THandler)handler);
  318. }
  319. );
  320. }
  321. #endif
  322. private static IDisposable DoNetEventBind(object sender, EventInfo ei, Delegate newHandler)
  323. {
  324. ei.AddEventHandler(sender, newHandler);
  325. return Disposable.Create(() => ei.RemoveEventHandler(sender, newHandler));
  326. }
  327. }
  328. public static class ColllectionHelper
  329. {
  330. public static ObservableCollection<T> ToObservableCollection<T>(this IEnumerable<T> items)
  331. {
  332. return new ObservableCollection<T>(items);
  333. }
  334. public static TValue MatchOrDefault<TKey, TValue>(this IDictionary<TKey, TValue> dic, TKey key)
  335. {
  336. TValue val = default(TValue);
  337. dic.TryGetValue(key, out val);
  338. return val;
  339. }
  340. }
  341. #if WINDOWS_PHONE_7
  342. public class Lazy<T>
  343. {
  344. public Lazy(Func<T> factory)
  345. {
  346. _factory =()=>
  347. {
  348. lock(this)
  349. {
  350. if(_value.Equals(default(T)))
  351. {
  352. _value=_factory();
  353. }
  354. return _value;
  355. }
  356. };
  357. }
  358. T _value;
  359. Func<T> _factory;
  360. public T Value
  361. {
  362. get
  363. {
  364. return _value.Equals (default(T))?_factory():_value;
  365. }
  366. set
  367. {
  368. _value=value;
  369. }
  370. }
  371. }
  372. #endif
  373. public class ExpressionHelper
  374. {
  375. public static string GetPropertyName<TSubClassType, TProperty>(Expression<Func<TSubClassType, TProperty>> expression)
  376. {
  377. MemberExpression body = expression.Body as MemberExpression;
  378. var propName = (body.Member is PropertyInfo) ? body.Member.Name : string.Empty;
  379. return propName;
  380. }
  381. public static string GetPropertyName<TSubClassType>(Expression<Func<TSubClassType, object>> expression)
  382. {
  383. MemberExpression body = expression.Body as MemberExpression;
  384. if (body != null)
  385. {
  386. var propName = (body.Member is PropertyInfo) ? body.Member.Name : string.Empty;
  387. return propName;
  388. }
  389. var exp2 = expression.Body as System.Linq.Expressions.UnaryExpression;
  390. if (exp2 != null)
  391. {
  392. body = exp2.Operand as MemberExpression;
  393. var propName = (body.Member is PropertyInfo) ? body.Member.Name : string.Empty;
  394. return propName;
  395. }
  396. else
  397. {
  398. throw new Exception();
  399. }
  400. }
  401. }
  402. #if SILVERLIGHT_5||WINDOWS_PHONE_8||WINDOWS_PHONE_7
  403. public class ConcurrentDictionary<TK, TV> : Dictionary<TK, TV>
  404. {
  405. public TV GetOrAdd(TK key, Func<TK, TV> factory)
  406. {
  407. TV rval = default(TV);
  408. if (!base.TryGetValue(key, out rval))
  409. {
  410. lock (this)
  411. {
  412. if (!base.TryGetValue(key, out rval))
  413. {
  414. rval = factory(key);
  415. base.Add(key, rval);
  416. }
  417. }
  418. }
  419. return rval;
  420. }
  421. }
  422. #endif
  423. /// <summary>
  424. /// Provides a task scheduler that ensures a maximum concurrency level while
  425. /// running on top of the ThreadPool.
  426. /// </summary>
  427. public class LimitedConcurrencyLevelTaskScheduler : TaskScheduler
  428. {
  429. /// <summary>Whether the current thread is processing work items.</summary>
  430. [ThreadStatic]
  431. private static bool _currentThreadIsProcessingItems;
  432. /// <summary>The list of tasks to be executed.</summary>
  433. private readonly LinkedList<Task> _tasks = new LinkedList<Task>(); // protected by lock(_tasks)
  434. /// <summary>The maximum concurrency level allowed by this scheduler.</summary>
  435. private readonly int _maxDegreeOfParallelism;
  436. /// <summary>Whether the scheduler is currently processing work items.</summary>
  437. private int _delegatesQueuedOrRunning = 0; // protected by lock(_tasks)
  438. /// <summary>
  439. /// Initializes an instance of the LimitedConcurrencyLevelTaskScheduler class with the
  440. /// specified degree of parallelism.
  441. /// </summary>
  442. /// <param name="maxDegreeOfParallelism">The maximum degree of parallelism provided by this scheduler.</param>
  443. public LimitedConcurrencyLevelTaskScheduler(int maxDegreeOfParallelism)
  444. {
  445. if (maxDegreeOfParallelism < 1) throw new ArgumentOutOfRangeException("maxDegreeOfParallelism");
  446. _maxDegreeOfParallelism = maxDegreeOfParallelism;
  447. }
  448. /// <summary>Queues a task to the scheduler.</summary>
  449. /// <param name="task">The task to be queued.</param>
  450. [SecurityCritical]
  451. protected sealed override void QueueTask(Task task)
  452. {
  453. // Add the task to the list of tasks to be processed. If there aren't enough
  454. // delegates currently queued or running to process tasks, schedule another.
  455. lock (_tasks)
  456. {
  457. _tasks.AddLast(task);
  458. if (_delegatesQueuedOrRunning < _maxDegreeOfParallelism)
  459. {
  460. ++_delegatesQueuedOrRunning;
  461. NotifyThreadPoolOfPendingWork();
  462. }
  463. }
  464. }
  465. /// <summary>
  466. /// Informs the ThreadPool that there's work to be executed for this scheduler.
  467. /// </summary>
  468. private async void NotifyThreadPoolOfPendingWork()
  469. {
  470. #if NETFX_CORE
  471. await ThreadPool.RunAsync((_1) =>
  472. #else
  473. await TaskExHelper.Yield();
  474. ThreadPool.QueueUserWorkItem(_ =>
  475. #endif
  476. {
  477. // Note that the current thread is now processing work items.
  478. // This is necessary to enable inlining of tasks into this thread.
  479. _currentThreadIsProcessingItems = true;
  480. try
  481. {
  482. // Process all available items in the queue.
  483. while (true)
  484. {
  485. Task item;
  486. lock (_tasks)
  487. {
  488. // When there are no more items to be processed,
  489. // note that we're done processing, and get out.
  490. if (_tasks.Count == 0)
  491. {
  492. --_delegatesQueuedOrRunning;
  493. break;
  494. }
  495. // Get the next item from the queue
  496. item = _tasks.First.Value;
  497. _tasks.RemoveFirst();
  498. }
  499. // Execute the task we pulled out of the queue
  500. base.TryExecuteTask(item);
  501. }
  502. }
  503. // We're done processing items on the current thread
  504. finally { _currentThreadIsProcessingItems = false; }
  505. #if NETFX_CORE
  506. });
  507. #else
  508. }, null);
  509. #endif
  510. }
  511. /// <summary>Attempts to execute the specified task on the current thread.</summary>
  512. /// <param name="task">The task to be executed.</param>
  513. /// <param name="taskWasPreviouslyQueued"></param>
  514. /// <returns>Whether the task could be executed on the current thread.</returns>
  515. [SecurityCritical]
  516. protected sealed override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued)
  517. {
  518. // If this thread isn't already processing a task, we don't support inlining
  519. if (!_currentThreadIsProcessingItems) return false;
  520. // If the task was previously queued, remove it from the queue
  521. if (taskWasPreviouslyQueued) TryDequeue(task);
  522. // Try to run the task.
  523. return base.TryExecuteTask(task);
  524. }
  525. /// <summary>Attempts to remove a previously scheduled task from the scheduler.</summary>
  526. /// <param name="task">The task to be removed.</param>
  527. /// <returns>Whether the task could be found and removed.</returns>
  528. [SecurityCritical]
  529. protected sealed override bool TryDequeue(Task task)
  530. {
  531. lock (_tasks) return _tasks.Remove(task);
  532. }
  533. /// <summary>Gets the maximum concurrency level supported by this scheduler.</summary>
  534. public sealed override int MaximumConcurrencyLevel { get { return _maxDegreeOfParallelism; } }
  535. /// <summary>Gets an enumerable of the tasks currently scheduled on this scheduler.</summary>
  536. /// <returns>An enumerable of the tasks currently scheduled.</returns>
  537. [SecurityCritical]
  538. protected sealed override IEnumerable<Task> GetScheduledTasks()
  539. {
  540. bool lockTaken = false;
  541. try
  542. {
  543. Monitor.TryEnter(_tasks, ref lockTaken);
  544. if (lockTaken) return _tasks.ToArray();
  545. else throw new NotSupportedException();
  546. }
  547. finally
  548. {
  549. if (lockTaken) Monitor.Exit(_tasks);
  550. }
  551. }
  552. }
  553. }
  554. }