PageRenderTime 6ms CodeModel.GetById 2ms app.highlight 17ms RepoModel.GetById 1ms app.codeStats 1ms

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