XamlPlatformProvider.cs 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251
  1. namespace Caliburn.Micro {
  2. using System;
  3. using System.Collections.Generic;
  4. using System.Threading.Tasks;
  5. #if WinRT
  6. using System.Reflection;
  7. using Windows.UI.Core;
  8. using Windows.UI.Xaml;
  9. #else
  10. using System.Windows;
  11. using System.Windows.Threading;
  12. using Caliburn.Micro.Core;
  13. #endif
  14. /// <summary>
  15. /// A <see cref="IPlatformProvider"/> implementation for the XAML platfrom.
  16. /// </summary>
  17. public class XamlPlatformProvider : IPlatformProvider {
  18. #if WinRT
  19. private CoreDispatcher dispatcher;
  20. #else
  21. private Dispatcher dispatcher;
  22. #endif
  23. /// <summary>
  24. /// Initializes a new instance of the <see cref="XamlPlatformProvider"/> class.
  25. /// </summary>
  26. public XamlPlatformProvider() {
  27. #if SILVERLIGHT
  28. dispatcher = System.Windows.Deployment.Current.Dispatcher;
  29. #elif WinRT
  30. dispatcher = Window.Current.Dispatcher;
  31. #else
  32. dispatcher = Dispatcher.CurrentDispatcher;
  33. #endif
  34. }
  35. /// <summary>
  36. /// Indicates whether or not the framework is in design-time mode.
  37. /// </summary>
  38. public bool InDesignMode {
  39. get { return View.InDesignMode; }
  40. }
  41. private void ValidateDispatcher() {
  42. if (dispatcher == null)
  43. throw new InvalidOperationException("Not initialized with dispatcher.");
  44. }
  45. private bool CheckAccess() {
  46. #if WinRT
  47. return dispatcher == null || Window.Current != null;
  48. #else
  49. return dispatcher == null || dispatcher.CheckAccess();
  50. #endif
  51. }
  52. /// <summary>
  53. /// Executes the action on the UI thread asynchronously.
  54. /// </summary>
  55. /// <param name="action">The action to execute.</param>
  56. public void BeginOnUIThread(System.Action action) {
  57. ValidateDispatcher();
  58. #if WinRT
  59. var dummy = dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => action());
  60. #else
  61. dispatcher.BeginInvoke(action);
  62. #endif
  63. }
  64. /// <summary>
  65. /// Executes the action on the UI thread asynchronously.
  66. /// </summary>
  67. /// <param name="action">The action to execute.</param>
  68. /// <returns></returns>
  69. public Task OnUIThreadAsync(System.Action action) {
  70. ValidateDispatcher();
  71. #if WinRT
  72. return dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => action()).AsTask();
  73. #elif NET45
  74. return dispatcher.InvokeAsync(action).Task;
  75. #else
  76. var taskSource = new TaskCompletionSource<object>();
  77. System.Action method = () => {
  78. try {
  79. action();
  80. taskSource.SetResult(null);
  81. }
  82. catch(Exception ex) {
  83. taskSource.SetException(ex);
  84. }
  85. };
  86. dispatcher.BeginInvoke(method);
  87. return taskSource.Task;
  88. #endif
  89. }
  90. /// <summary>
  91. /// Executes the action on the UI thread.
  92. /// </summary>
  93. /// <param name="action">The action to execute.</param>
  94. /// <exception cref="System.NotImplementedException"></exception>
  95. public void OnUIThread(System.Action action) {
  96. if (CheckAccess())
  97. action();
  98. else {
  99. #if WinRT
  100. dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => action()).AsTask().Wait();
  101. #elif NET
  102. Exception exception = null;
  103. System.Action method = () => {
  104. try {
  105. action();
  106. }
  107. catch(Exception ex) {
  108. exception = ex;
  109. }
  110. };
  111. dispatcher.Invoke(method);
  112. if (exception != null)
  113. throw new System.Reflection.TargetInvocationException("An error occurred while dispatching a call to the UI Thread", exception);
  114. #else
  115. var waitHandle = new System.Threading.ManualResetEvent(false);
  116. Exception exception = null;
  117. System.Action method = () => {
  118. try {
  119. action();
  120. }
  121. catch (Exception ex) {
  122. exception = ex;
  123. }
  124. waitHandle.Set();
  125. };
  126. dispatcher.BeginInvoke(method);
  127. waitHandle.WaitOne();
  128. if (exception != null)
  129. throw new System.Reflection.TargetInvocationException("An error occurred while dispatching a call to the UI Thread", exception);
  130. #endif
  131. }
  132. }
  133. /// <summary>
  134. /// Used to retrieve the root, non-framework-created view.
  135. /// </summary>
  136. /// <param name="view">The view to search.</param>
  137. /// <returns>
  138. /// The root element that was not created by the framework.
  139. /// </returns>
  140. /// <remarks>
  141. /// In certain instances the services create UI elements.
  142. /// For example, if you ask the window manager to show a UserControl as a dialog, it creates a window to host the UserControl in.
  143. /// The WindowManager marks that element as a framework-created element so that it can determine what it created vs. what was intended by the developer.
  144. /// Calling GetFirstNonGeneratedView allows the framework to discover what the original element was.
  145. /// </remarks>
  146. public object GetFirstNonGeneratedView(object view) {
  147. return View.GetFirstNonGeneratedView(view);
  148. }
  149. private static readonly DependencyProperty PreviouslyAttachedProperty = DependencyProperty.RegisterAttached(
  150. "PreviouslyAttached",
  151. typeof (bool),
  152. typeof (XamlPlatformProvider),
  153. null
  154. );
  155. /// <summary>
  156. /// Executes the handler the fist time the view is loaded.
  157. /// </summary>
  158. /// <param name="view">The view.</param>
  159. /// <param name="handler">The handler.</param>
  160. public void ExecuteOnFirstLoad(object view, Action<object> handler) {
  161. var element = view as FrameworkElement;
  162. if (element != null && !(bool) element.GetValue(PreviouslyAttachedProperty)) {
  163. element.SetValue(PreviouslyAttachedProperty, true);
  164. View.ExecuteOnLoad(element, (s, e) => handler(s));
  165. }
  166. }
  167. /// <summary>
  168. /// Executes the handler the next time the view's LayoutUpdated event fires.
  169. /// </summary>
  170. /// <param name="view">The view.</param>
  171. /// <param name="handler">The handler.</param>
  172. public void ExecuteOnLayoutUpdated(object view, Action<object> handler) {
  173. var element = view as FrameworkElement;
  174. if (element != null) {
  175. View.ExecuteOnLayoutUpdated(element, (s, e) => handler(s));
  176. }
  177. }
  178. /// <summary>
  179. /// Get the close action for the specified view model.
  180. /// </summary>
  181. /// <param name="viewModel">The view model to close.</param>
  182. /// <param name="views">The associated views.</param>
  183. /// <param name="dialogResult">The dialog result.</param>
  184. /// <returns>
  185. /// An <see cref="Action" /> to close the view model.
  186. /// </returns>
  187. /// <exception cref="System.NotImplementedException"></exception>
  188. public System.Action GetViewCloseAction(object viewModel, ICollection<object> views, bool? dialogResult) {
  189. var child = viewModel as IChild;
  190. if (child != null) {
  191. var conductor = child.Parent as IConductor;
  192. if (conductor != null) {
  193. return () => conductor.CloseItem(viewModel);
  194. }
  195. }
  196. foreach (var contextualView in views) {
  197. var viewType = contextualView.GetType();
  198. #if WinRT
  199. var closeMethod = viewType.GetRuntimeMethod("Close", new Type[0]);
  200. #else
  201. var closeMethod = viewType.GetMethod("Close");
  202. #endif
  203. if (closeMethod != null)
  204. return () => {
  205. #if !SILVERLIGHT && !WinRT
  206. var isClosed = false;
  207. if (dialogResult != null) {
  208. var resultProperty = contextualView.GetType().GetProperty("DialogResult");
  209. if (resultProperty != null) {
  210. resultProperty.SetValue(contextualView, dialogResult, null);
  211. isClosed = true;
  212. }
  213. }
  214. if (!isClosed) {
  215. closeMethod.Invoke(contextualView, null);
  216. }
  217. #else
  218. closeMethod.Invoke(contextualView, null);
  219. #endif
  220. };
  221. #if WinRT
  222. var isOpenProperty = viewType.GetRuntimeProperty("IsOpen");
  223. #else
  224. var isOpenProperty = viewType.GetProperty("IsOpen");
  225. #endif
  226. if (isOpenProperty != null) {
  227. return () => isOpenProperty.SetValue(contextualView, false, null);
  228. }
  229. }
  230. return () => LogManager.GetLog(typeof(Screen)).Info("TryClose requires a parent IConductor or a view with a Close method or IsOpen property.");
  231. }
  232. }
  233. }