PageRenderTime 188ms CodeModel.GetById 28ms RepoModel.GetById 0ms app.codeStats 0ms

/src/Caliburn.Micro.Silverlight/View.cs

https://github.com/paulcbetts/CaliburnMicro
C# | 263 lines | 161 code | 32 blank | 70 comment | 20 complexity | 9782b17912881d1e04115a64213d3f23 MD5 | raw file
  1. namespace Caliburn.Micro
  2. {
  3. using System;
  4. using System.Linq;
  5. using System.Windows;
  6. using System.Windows.Controls;
  7. using System.Windows.Markup;
  8. /// <summary>
  9. /// Hosts attached properties related to view models.
  10. /// </summary>
  11. public static class View {
  12. static readonly ILog Log = LogManager.GetLog(typeof(View));
  13. static readonly ContentPropertyAttribute DefaultContentProperty = new ContentPropertyAttribute("Content");
  14. /// <summary>
  15. /// The default view context.
  16. /// </summary>
  17. public static readonly object DefaultContext = new object();
  18. /// <summary>
  19. /// A dependency property which allows the framework to track whether a certain element has already been loaded in certain scenarios.
  20. /// </summary>
  21. public static readonly DependencyProperty IsLoadedProperty =
  22. DependencyProperty.RegisterAttached(
  23. "IsLoaded",
  24. typeof(bool),
  25. typeof(View),
  26. new PropertyMetadata(false)
  27. );
  28. /// <summary>
  29. /// A dependency property which marks an element as a name scope root.
  30. /// </summary>
  31. public static readonly DependencyProperty IsScopeRootProperty =
  32. DependencyProperty.RegisterAttached(
  33. "IsScopeRoot",
  34. typeof(bool),
  35. typeof(View),
  36. new PropertyMetadata(false)
  37. );
  38. /// <summary>
  39. /// A dependency property which allows the override of convention application behavior.
  40. /// </summary>
  41. public static readonly DependencyProperty ApplyConventionsProperty =
  42. DependencyProperty.RegisterAttached(
  43. "ApplyConventions",
  44. typeof(bool?),
  45. typeof(View),
  46. null
  47. );
  48. /// <summary>
  49. /// A dependency property for assigning a context to a particular portion of the UI.
  50. /// </summary>
  51. public static readonly DependencyProperty ContextProperty =
  52. DependencyProperty.RegisterAttached(
  53. "Context",
  54. typeof(object),
  55. typeof(View),
  56. new PropertyMetadata(OnContextChanged)
  57. );
  58. /// <summary>
  59. /// A dependency property for attaching a model to the UI.
  60. /// </summary>
  61. public static DependencyProperty ModelProperty =
  62. DependencyProperty.RegisterAttached(
  63. "Model",
  64. typeof(object),
  65. typeof(View),
  66. new PropertyMetadata(OnModelChanged)
  67. );
  68. /// <summary>
  69. /// Used by the framework to indicate that this element was generated.
  70. /// </summary>
  71. public static readonly DependencyProperty IsGeneratedProperty =
  72. DependencyProperty.RegisterAttached(
  73. "IsGenerated",
  74. typeof(bool),
  75. typeof(View),
  76. new PropertyMetadata(false, null)
  77. );
  78. /// <summary>
  79. /// Executes the handler immediately if the element is loaded, otherwise wires it to the Loaded event.
  80. /// </summary>
  81. /// <param name="element">The element.</param>
  82. /// <param name="handler">The handler.</param>
  83. /// <returns>true if the handler was executed immediately; false otherwise</returns>
  84. public static bool ExecuteOnLoad(FrameworkElement element, RoutedEventHandler handler) {
  85. #if SILVERLIGHT
  86. if((bool)element.GetValue(IsLoadedProperty))
  87. #else
  88. if(element.IsLoaded)
  89. #endif
  90. {
  91. handler(element, new RoutedEventArgs());
  92. return true;
  93. }
  94. else {
  95. RoutedEventHandler loaded = null;
  96. loaded = (s, e) => {
  97. #if SILVERLIGHT
  98. element.SetValue(IsLoadedProperty, true);
  99. #endif
  100. handler(s, e);
  101. element.Loaded -= loaded;
  102. };
  103. element.Loaded += loaded;
  104. return false;
  105. }
  106. }
  107. /// <summary>
  108. /// Used to retrieve the root, non-framework-created view.
  109. /// </summary>
  110. /// <param name="view">The view to search.</param>
  111. /// <returns>The root element that was not created by the framework.</returns>
  112. /// <remarks>In certain instances the services create UI elements.
  113. /// For example, if you ask the window manager to show a UserControl as a dialog, it creates a window to host the UserControl in.
  114. /// 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.
  115. /// Calling GetFirstNonGeneratedView allows the framework to discover what the original element was.
  116. /// </remarks>
  117. public static Func<object, object> GetFirstNonGeneratedView = view => {
  118. var dependencyObject = view as DependencyObject;
  119. if(dependencyObject == null)
  120. return view;
  121. if((bool)dependencyObject.GetValue(IsGeneratedProperty)) {
  122. if(dependencyObject is ContentControl)
  123. return ((ContentControl)dependencyObject).Content;
  124. var type = dependencyObject.GetType();
  125. var contentProperty = type.GetAttributes<ContentPropertyAttribute>(true)
  126. .FirstOrDefault() ?? DefaultContentProperty;
  127. return type.GetProperty(contentProperty.Name)
  128. .GetValue(dependencyObject, null);
  129. }
  130. return dependencyObject;
  131. };
  132. /// <summary>
  133. /// Gets the convention application behavior.
  134. /// </summary>
  135. /// <param name="d">The element the property is attached to.</param>
  136. /// <returns>Whether or not to apply conventions.</returns>
  137. public static bool? GetApplyConventions(DependencyObject d)
  138. {
  139. return (bool?)d.GetValue(ApplyConventionsProperty);
  140. }
  141. /// <summary>
  142. /// Sets the convention application behavior.
  143. /// </summary>
  144. /// <param name="d">The element to attach the property to.</param>
  145. /// <param name="value">Whether or not to apply conventions.</param>
  146. public static void SetApplyConventions(DependencyObject d, bool? value)
  147. {
  148. d.SetValue(ApplyConventionsProperty, value);
  149. }
  150. /// <summary>
  151. /// Sets the model.
  152. /// </summary>
  153. /// <param name="d">The element to attach the model to.</param>
  154. /// <param name="value">The model.</param>
  155. public static void SetModel(DependencyObject d, object value)
  156. {
  157. d.SetValue(ModelProperty, value);
  158. }
  159. /// <summary>
  160. /// Gets the model.
  161. /// </summary>
  162. /// <param name="d">The element the model is attached to.</param>
  163. /// <returns>The model.</returns>
  164. public static object GetModel(DependencyObject d)
  165. {
  166. return d.GetValue(ModelProperty);
  167. }
  168. /// <summary>
  169. /// Gets the context.
  170. /// </summary>
  171. /// <param name="d">The element the context is attached to.</param>
  172. /// <returns>The context.</returns>
  173. public static object GetContext(DependencyObject d)
  174. {
  175. return d.GetValue(ContextProperty);
  176. }
  177. /// <summary>
  178. /// Sets the context.
  179. /// </summary>
  180. /// <param name="d">The element to attach the context to.</param>
  181. /// <param name="value">The context.</param>
  182. public static void SetContext(DependencyObject d, object value)
  183. {
  184. d.SetValue(ContextProperty, value);
  185. }
  186. private static void OnModelChanged(DependencyObject targetLocation, DependencyPropertyChangedEventArgs args)
  187. {
  188. if (args.OldValue == args.NewValue)
  189. return;
  190. if (args.NewValue != null)
  191. {
  192. var context = GetContext(targetLocation);
  193. var view = ViewLocator.LocateForModel(args.NewValue, targetLocation, context);
  194. SetContentProperty(targetLocation, view);
  195. ViewModelBinder.Bind(args.NewValue, view, context);
  196. }
  197. else SetContentProperty(targetLocation, args.NewValue);
  198. }
  199. private static void OnContextChanged(DependencyObject targetLocation, DependencyPropertyChangedEventArgs e)
  200. {
  201. if (e.OldValue == e.NewValue)
  202. return;
  203. var model = GetModel(targetLocation);
  204. if (model == null)
  205. return;
  206. var view = ViewLocator.LocateForModel(model, targetLocation, e.NewValue);
  207. SetContentProperty(targetLocation, view);
  208. ViewModelBinder.Bind(model, view, e.NewValue);
  209. }
  210. private static void SetContentProperty(object targetLocation, object view)
  211. {
  212. var fe = view as FrameworkElement;
  213. if (fe != null && fe.Parent != null)
  214. SetContentPropertyCore(fe.Parent, null);
  215. SetContentPropertyCore(targetLocation, view);
  216. }
  217. private static void SetContentPropertyCore(object targetLocation, object view)
  218. {
  219. try {
  220. var type = targetLocation.GetType();
  221. var contentProperty = type.GetAttributes<ContentPropertyAttribute>(true)
  222. .FirstOrDefault() ?? DefaultContentProperty;
  223. type.GetProperty(contentProperty.Name)
  224. .SetValue(targetLocation, view, null);
  225. }
  226. catch(Exception e) {
  227. Log.Error(e);
  228. }
  229. }
  230. }
  231. }