PageRenderTime 37ms CodeModel.GetById 12ms RepoModel.GetById 1ms app.codeStats 0ms

/V2/trunk/RI/Silverlight/StockTraderRI.Infrastructure.Silverlight/SelectorExtensions.cs

#
C# | 182 lines | 115 code | 18 blank | 49 comment | 23 complexity | 332f207b0e23ca8e6d112ae2b0193253 MD5 | raw file
  1. //===================================================================================
  2. // Microsoft patterns & practices
  3. // Composite Application Guidance for Windows Presentation Foundation and Silverlight
  4. //===================================================================================
  5. // Copyright (c) Microsoft Corporation. All rights reserved.
  6. // THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY
  7. // OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT
  8. // LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
  9. // FITNESS FOR A PARTICULAR PURPOSE.
  10. //===================================================================================
  11. // The example companies, organizations, products, domain names,
  12. // e-mail addresses, logos, people, places, and events depicted
  13. // herein are fictitious. No association with any real company,
  14. // organization, product, domain name, email address, logo, person,
  15. // places, or events is intended or should be inferred.
  16. //===================================================================================
  17. using System;
  18. using System.Collections.Generic;
  19. using System.Globalization;
  20. using System.Linq;
  21. using System.Reflection;
  22. using System.Windows;
  23. using System.Windows.Controls;
  24. using System.Windows.Controls.Primitives;
  25. using StockTraderRI.Infrastructure.Properties;
  26. namespace StockTraderRI.Infrastructure
  27. {
  28. /// <summary>
  29. /// Defines the Attached Behavior needed to keep synchronized the selected value
  30. /// of a <see cref="Selector"/> with a bound property.
  31. /// </summary>
  32. /// <remarks>This is to workaround the missing SelectedItem property that is present in WPF but not in Silverlight.</remarks>
  33. public static class SelectorExtensions
  34. {
  35. /// <summary>
  36. /// The property bound to the <see cref="Selector"/>'s selected value.
  37. /// </summary>
  38. public static readonly DependencyProperty SelectedValueProperty =
  39. DependencyProperty.RegisterAttached("SelectedValue", typeof(object), typeof(SelectorExtensions), new PropertyMetadata(SelectedValueChanged));
  40. /// <summary>
  41. /// The path to the bound property getter.
  42. /// </summary>
  43. public static readonly DependencyProperty SelectedValuePathProperty =
  44. DependencyProperty.RegisterAttached("SelectedValuePath", typeof(string), typeof(SelectorExtensions), new PropertyMetadata(SelectedValuePathChanged));
  45. private static readonly Dictionary<string, MethodInfo> cachedPropertyGetters = new Dictionary<string, MethodInfo>();
  46. /// <summary>
  47. /// Gets the <see cref="SelectedValueProperty"/> value.
  48. /// </summary>
  49. /// <param name="dependencyObject">The <see cref="DependencyObject"/> on which the attached property is set.</param>
  50. /// <returns>The value of the <see cref="SelectedValueProperty"/> property.</returns>
  51. public static object GetSelectedValue(DependencyObject dependencyObject)
  52. {
  53. return dependencyObject.GetValue(SelectedValueProperty);
  54. }
  55. /// <summary>
  56. /// Sets the <see cref="SelectedValueProperty"/> value.
  57. /// </summary>
  58. /// <param name="dependencyObject">The <see cref="DependencyObject"/> on which the attached property will be set.</param>
  59. /// <param name="value">Value to set to <see cref="SelectedValueProperty"/> attached property.</param>
  60. public static void SetSelectedValue(DependencyObject dependencyObject, object value)
  61. {
  62. dependencyObject.SetValue(SelectedValueProperty, value);
  63. }
  64. /// <summary>
  65. /// Gets the <see cref="SelectedValuePathProperty"/> value.
  66. /// </summary>
  67. /// <param name="dependencyObject">The <see cref="DependencyObject"/> on which the attached property is set.</param>
  68. /// <returns>The value of the <see cref="SelectedValuePathProperty"/> property.</returns>
  69. public static string GetSelectedValuePath(DependencyObject dependencyObject)
  70. {
  71. return dependencyObject.GetValue(SelectedValuePathProperty) as string;
  72. }
  73. /// <summary>
  74. /// Sets the <see cref="SelectedValuePathProperty"/> value.
  75. /// </summary>
  76. /// <param name="dependencyObject">The <see cref="DependencyObject"/> on which the attached property will be set.</param>
  77. /// <param name="value">Value to set to <see cref="SelectedValuePathProperty"/> attached property.</param>
  78. public static void SetSelectedValuePath(DependencyObject dependencyObject, string value)
  79. {
  80. dependencyObject.SetValue(SelectedValuePathProperty, value);
  81. }
  82. private static void SelectedValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
  83. {
  84. object newValue = e.NewValue;
  85. if (!Equals(newValue, e.OldValue))
  86. {
  87. Selector selector = d as Selector;
  88. if (selector != null)
  89. {
  90. SyncronizeSelectedItem(selector, newValue);
  91. }
  92. }
  93. }
  94. private static void SyncronizeSelectedItem(Selector selector, object value)
  95. {
  96. string selectedValuePath = GetSelectedValuePath(selector);
  97. if (!string.IsNullOrEmpty(selectedValuePath))
  98. {
  99. if (selector.SelectedItem == null
  100. || !Equals(GetValueForPath(selector.SelectedItem, selectedValuePath), value))
  101. {
  102. object selectedItem =
  103. selector.Items.FirstOrDefault(
  104. item => Equals(GetValueForPath(item, selectedValuePath), value));
  105. selector.SelectedItem = selectedItem;
  106. }
  107. }
  108. }
  109. private static void SelectedValuePathChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
  110. {
  111. Selector selector = d as Selector;
  112. if (selector != null)
  113. {
  114. if (e.OldValue == null && e.NewValue != null)
  115. {
  116. // Subscribes to selection changes in the Selector.
  117. selector.SelectionChanged += SelectorSelectionChanged;
  118. }
  119. if (e.OldValue != null && e.NewValue == null)
  120. {
  121. // Unsubscribes to clean up.
  122. selector.SelectionChanged -= SelectorSelectionChanged;
  123. }
  124. SyncronizeSelectedItem(selector, GetSelectedValue(selector));
  125. }
  126. }
  127. private static void SelectorSelectionChanged(object sender, SelectionChangedEventArgs e)
  128. {
  129. Selector selector = (Selector)sender;
  130. object selectedItem = selector.SelectedItem;
  131. string selectedValuePath = GetSelectedValuePath(selector);
  132. object selectedValue = GetValueForPath(selectedItem, selectedValuePath);
  133. if (!Equals(GetSelectedValue(selector), selectedValue))
  134. {
  135. SetSelectedValue(selector, selectedValue);
  136. }
  137. }
  138. private static object GetValueForPath(object instance, string valuePath)
  139. {
  140. MethodInfo propertyGetter = GetPropertyGetterForType(instance.GetType(), valuePath);
  141. object returnValue = propertyGetter.Invoke(instance, null);
  142. return returnValue;
  143. }
  144. private static MethodInfo GetPropertyGetterForType(Type type, string memberName)
  145. {
  146. string hashKey = string.Format(CultureInfo.InvariantCulture, "{0}&{1}", type.AssemblyQualifiedName, memberName);
  147. MethodInfo methodInfo;
  148. if (!cachedPropertyGetters.TryGetValue(hashKey, out methodInfo))
  149. {
  150. PropertyInfo property = type.GetProperty(memberName);
  151. if (property != null)
  152. {
  153. methodInfo = property.GetGetMethod();
  154. }
  155. cachedPropertyGetters.Add(hashKey, methodInfo);
  156. }
  157. if (methodInfo == null)
  158. {
  159. throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, Resources.SelectorExtensionCannotResolveMember, type.FullName, memberName, typeof(SelectorExtensions).Name));
  160. }
  161. return methodInfo;
  162. }
  163. }
  164. }