PageRenderTime 29ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/src/Catel.SL4/Windows/Helpers/SetterValueBindingHelper.cs

#
C# | 257 lines | 164 code | 17 blank | 76 comment | 38 complexity | 510c1ceda4a67ffd1e6d0678b900ce54 MD5 | raw file
Possible License(s): CC-BY-SA-3.0
  1. // Copyright (C) Microsoft Corporation. All Rights Reserved.
  2. // This code released under the terms of the Microsoft Public License
  3. // (Ms-PL, http://opensource.org/licenses/ms-pl.html).
  4. namespace Catel.Windows
  5. {
  6. using System;
  7. using System.Collections.Generic;
  8. using System.Collections.ObjectModel;
  9. using System.Diagnostics.CodeAnalysis;
  10. using System.Globalization;
  11. using System.Linq;
  12. using System.Reflection;
  13. using System.Windows;
  14. using System.Windows.Controls;
  15. using System.Windows.Data;
  16. using System.Windows.Markup;
  17. /// <summary>
  18. /// Class that implements a workaround for a Silverlight XAML parser
  19. /// limitation that prevents the following syntax from working:
  20. /// &lt;Setter Property="IsSelected" Value="{Binding IsSelected}"/&gt;.
  21. /// </summary>
  22. /// <remarks>
  23. /// This code originally comes from http://blogs.msdn.com/b/delay/archive/2009/11/02/as-the-platform-evolves-so-do-the-workarounds-better-settervaluebindinghelper-makes-silverlight-setters-better-er.aspx.
  24. /// </remarks>
  25. [ContentProperty("Values")]
  26. public class SetterValueBindingHelper
  27. {
  28. /// <summary>
  29. /// Gets or sets an optional type parameter used to specify the type
  30. /// of an attached DependencyProperty as an assembly-qualified name,
  31. /// full name, or short name.
  32. /// </summary>
  33. [SuppressMessage("Microsoft.Naming", "CA1721:PropertyNamesShouldNotMatchGetMethods",
  34. Justification = "Unambiguous in XAML.")]
  35. public string Type { get; set; }
  36. /// <summary>
  37. /// Gets or sets a property name for the normal/attached
  38. /// DependencyProperty on which to set the Binding.
  39. /// </summary>
  40. public string Property { get; set; }
  41. /// <summary>
  42. /// Gets or sets a Binding to set on the specified property.
  43. /// </summary>
  44. public Binding Binding { get; set; }
  45. /// <summary>
  46. /// Gets a Collection of SetterValueBindingHelper instances to apply
  47. /// to the target element.
  48. /// </summary>
  49. /// <remarks>
  50. /// Used when multiple Bindings need to be applied to the same element.
  51. /// </remarks>
  52. public Collection<SetterValueBindingHelper> Values
  53. {
  54. get
  55. {
  56. // Defer creating collection until needed
  57. if (null == _values)
  58. {
  59. _values = new Collection<SetterValueBindingHelper>();
  60. }
  61. return _values;
  62. }
  63. }
  64. /// <summary>
  65. /// Backing store for the Values property.
  66. /// </summary>
  67. private Collection<SetterValueBindingHelper> _values;
  68. /// <summary>
  69. /// Gets the value of the PropertyBinding attached DependencyProperty.
  70. /// </summary>
  71. /// <param name="element">Element for which to get the property.</param>
  72. /// <returns>Value of PropertyBinding attached DependencyProperty.</returns>
  73. [SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters",
  74. Justification = "SetBinding is only available on FrameworkElement.")]
  75. public static SetterValueBindingHelper GetPropertyBinding(FrameworkElement element)
  76. {
  77. if (null == element)
  78. {
  79. throw new ArgumentNullException("element");
  80. }
  81. return (SetterValueBindingHelper)element.GetValue(PropertyBindingProperty);
  82. }
  83. /// <summary>
  84. /// Sets the value of the PropertyBinding attached DependencyProperty.
  85. /// </summary>
  86. /// <param name="element">Element on which to set the property.</param>
  87. /// <param name="value">Value forPropertyBinding attached DependencyProperty.</param>
  88. [SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters",
  89. Justification = "SetBinding is only available on FrameworkElement.")]
  90. public static void SetPropertyBinding(FrameworkElement element, SetterValueBindingHelper value)
  91. {
  92. if (null == element)
  93. {
  94. throw new ArgumentNullException("element");
  95. }
  96. element.SetValue(PropertyBindingProperty, value);
  97. }
  98. /// <summary>
  99. /// PropertyBinding attached DependencyProperty.
  100. /// </summary>
  101. public static readonly DependencyProperty PropertyBindingProperty =
  102. DependencyProperty.RegisterAttached(
  103. "PropertyBinding",
  104. typeof(SetterValueBindingHelper),
  105. typeof(SetterValueBindingHelper),
  106. new PropertyMetadata(null, OnPropertyBindingPropertyChanged));
  107. /// <summary>
  108. /// Change handler for the PropertyBinding attached DependencyProperty.
  109. /// </summary>
  110. /// <param name="d">Object on which the property was changed.</param>
  111. /// <param name="e">Property change arguments.</param>
  112. private static void OnPropertyBindingPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
  113. {
  114. // Get/validate parameters
  115. var element = (FrameworkElement)d;
  116. var item = (SetterValueBindingHelper)e.NewValue;
  117. if (null != item)
  118. {
  119. // Item value present
  120. if ((null == item.Values) || (0 == item.Values.Count))
  121. {
  122. // No children; apply the relevant binding
  123. ApplyBinding(element, item);
  124. }
  125. else
  126. {
  127. // Apply the bindings of each child
  128. foreach (var child in item.Values)
  129. {
  130. if ((null != item.Property) || (null != item.Binding))
  131. {
  132. throw new ArgumentException(
  133. "A SetterValueBindingHelper with Values may not have its Property or Binding set.");
  134. }
  135. if (0 != child.Values.Count)
  136. {
  137. throw new ArgumentException(
  138. "Values of a SetterValueBindingHelper may not have Values themselves.");
  139. }
  140. ApplyBinding(element, child);
  141. }
  142. }
  143. }
  144. }
  145. /// <summary>
  146. /// Applies the Binding represented by the SetterValueBindingHelper.
  147. /// </summary>
  148. /// <param name="element">Element to apply the Binding to.</param>
  149. /// <param name="item">SetterValueBindingHelper representing the Binding.</param>
  150. private static void ApplyBinding(FrameworkElement element, SetterValueBindingHelper item)
  151. {
  152. if ((null == item.Property) || (null == item.Binding))
  153. {
  154. throw new ArgumentException(
  155. "SetterValueBindingHelper's Property and Binding must both be set to non-null values.");
  156. }
  157. // Get the type on which to set the Binding
  158. Type type = null;
  159. if (null == item.Type)
  160. {
  161. // No type specified; setting for the specified element
  162. type = element.GetType();
  163. }
  164. else
  165. {
  166. // Try to get the type from the type system
  167. type = System.Type.GetType(item.Type);
  168. if (null == type)
  169. {
  170. // Search for the type in the list of assemblies
  171. foreach (var assembly in AssembliesToSearch)
  172. {
  173. // Match on short or full name
  174. type = assembly.GetTypes()
  175. .Where(t => (t.FullName == item.Type) || (t.Name == item.Type))
  176. .FirstOrDefault();
  177. if (null != type)
  178. {
  179. // Found; done searching
  180. break;
  181. }
  182. }
  183. if (null == type)
  184. {
  185. // Unable to find the requested type anywhere
  186. throw new ArgumentException(
  187. string.Format(
  188. CultureInfo.CurrentCulture,
  189. "Unable to access type \"{0}\". Try using an assembly qualified type name.",
  190. item.Type));
  191. }
  192. }
  193. }
  194. // Get the DependencyProperty for which to set the Binding
  195. DependencyProperty property = null;
  196. var field = type.GetField(
  197. item.Property + "Property",
  198. BindingFlags.FlattenHierarchy | BindingFlags.Public | BindingFlags.Static);
  199. if (null != field)
  200. {
  201. property = field.GetValue(null) as DependencyProperty;
  202. }
  203. if (null == property)
  204. {
  205. // Unable to find the requsted property
  206. throw new ArgumentException(
  207. string.Format(
  208. CultureInfo.CurrentCulture,
  209. "Unable to access DependencyProperty \"{0}\" on type \"{1}\".",
  210. item.Property,
  211. type.Name));
  212. }
  213. // Set the specified Binding on the specified property
  214. element.SetBinding(property, item.Binding);
  215. }
  216. /// <summary>
  217. /// Gets a sequence of assemblies to search for the provided type name.
  218. /// </summary>
  219. private static IEnumerable<Assembly> AssembliesToSearch
  220. {
  221. get
  222. {
  223. // Start with the System.Windows assembly (home of all core controls)
  224. yield return typeof(Control).Assembly;
  225. #if SILVERLIGHT && !WINDOWS_PHONE
  226. // Fall back by trying each of the assemblies in the Deployment's Parts list
  227. foreach (var part in Deployment.Current.Parts)
  228. {
  229. var streamResourceInfo = Application.GetResourceStream(
  230. new Uri(part.Source, UriKind.Relative));
  231. using (var stream = streamResourceInfo.Stream)
  232. {
  233. yield return part.Load(stream);
  234. }
  235. }
  236. #endif
  237. }
  238. }
  239. }
  240. }