PageRenderTime 26ms CodeModel.GetById 12ms app.highlight 10ms 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//===================================================================================
 17using System;
 18using System.Collections.Generic;
 19using System.Globalization;
 20using System.Linq;
 21using System.Reflection;
 22using System.Windows;
 23using System.Windows.Controls;
 24using System.Windows.Controls.Primitives;
 25using StockTraderRI.Infrastructure.Properties;
 26
 27namespace StockTraderRI.Infrastructure
 28{
 29    /// <summary>
 30    /// Defines the Attached Behavior needed to keep synchronized the selected value 
 31    /// of a <see cref="Selector"/> with a bound property.
 32    /// </summary>
 33    /// <remarks>This is to workaround the missing SelectedItem property that is present in WPF but not in Silverlight.</remarks>
 34    public static class SelectorExtensions
 35    {
 36        /// <summary>
 37        /// The property bound to the <see cref="Selector"/>'s selected value.
 38        /// </summary>
 39        public static readonly DependencyProperty SelectedValueProperty =
 40                DependencyProperty.RegisterAttached("SelectedValue", typeof(object), typeof(SelectorExtensions), new PropertyMetadata(SelectedValueChanged));
 41
 42        /// <summary>
 43        /// The path to the bound property getter.
 44        /// </summary>
 45        public static readonly DependencyProperty SelectedValuePathProperty =
 46                DependencyProperty.RegisterAttached("SelectedValuePath", typeof(string), typeof(SelectorExtensions), new PropertyMetadata(SelectedValuePathChanged));
 47
 48        private static readonly Dictionary<string, MethodInfo> cachedPropertyGetters = new Dictionary<string, MethodInfo>();
 49
 50        /// <summary>
 51        /// Gets the <see cref="SelectedValueProperty"/> value.
 52        /// </summary>
 53        /// <param name="dependencyObject">The <see cref="DependencyObject"/> on which the attached property is set.</param>
 54        /// <returns>The value of the <see cref="SelectedValueProperty"/> property.</returns>
 55        public static object GetSelectedValue(DependencyObject dependencyObject)
 56        {
 57            return dependencyObject.GetValue(SelectedValueProperty);
 58        }
 59
 60        /// <summary>
 61        /// Sets the <see cref="SelectedValueProperty"/> value.
 62        /// </summary>
 63        /// <param name="dependencyObject">The <see cref="DependencyObject"/> on which the attached property will be set.</param>
 64        /// <param name="value">Value to set to <see cref="SelectedValueProperty"/> attached property.</param>
 65        public static void SetSelectedValue(DependencyObject dependencyObject, object value)
 66        {
 67            dependencyObject.SetValue(SelectedValueProperty, value);
 68        }
 69
 70        /// <summary>
 71        /// Gets the <see cref="SelectedValuePathProperty"/> value.
 72        /// </summary>
 73        /// <param name="dependencyObject">The <see cref="DependencyObject"/> on which the attached property is set.</param>
 74        /// <returns>The value of the <see cref="SelectedValuePathProperty"/> property.</returns>
 75        public static string GetSelectedValuePath(DependencyObject dependencyObject)
 76        {
 77            return dependencyObject.GetValue(SelectedValuePathProperty) as string;
 78        }
 79
 80        /// <summary>
 81        /// Sets the <see cref="SelectedValuePathProperty"/> value.
 82        /// </summary>
 83        /// <param name="dependencyObject">The <see cref="DependencyObject"/> on which the attached property will be set.</param>
 84        /// <param name="value">Value to set to <see cref="SelectedValuePathProperty"/> attached property.</param>
 85        public static void SetSelectedValuePath(DependencyObject dependencyObject, string value)
 86        {
 87            dependencyObject.SetValue(SelectedValuePathProperty, value);
 88        }
 89
 90        private static void SelectedValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
 91        {
 92            object newValue = e.NewValue;
 93            if (!Equals(newValue, e.OldValue))
 94            {
 95                Selector selector = d as Selector;
 96                if (selector != null)
 97                {
 98                    SyncronizeSelectedItem(selector, newValue);
 99                }
100            }
101        }
102
103        private static void SyncronizeSelectedItem(Selector selector, object value)
104        {
105            string selectedValuePath = GetSelectedValuePath(selector);
106            if (!string.IsNullOrEmpty(selectedValuePath))
107            {
108                if (selector.SelectedItem == null
109                    || !Equals(GetValueForPath(selector.SelectedItem, selectedValuePath), value))
110                {
111                    object selectedItem =
112                            selector.Items.FirstOrDefault(
113                                    item => Equals(GetValueForPath(item, selectedValuePath), value));
114                    selector.SelectedItem = selectedItem;
115                }
116            }
117        }
118
119        private static void SelectedValuePathChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
120        {
121            Selector selector = d as Selector;
122            if (selector != null)
123            {
124                if (e.OldValue == null && e.NewValue != null)
125                {
126                    // Subscribes to selection changes in the Selector.
127                    selector.SelectionChanged += SelectorSelectionChanged;
128                }
129
130                if (e.OldValue != null && e.NewValue == null)
131                {
132                    // Unsubscribes to clean up.
133                    selector.SelectionChanged -= SelectorSelectionChanged;
134                }
135
136                SyncronizeSelectedItem(selector, GetSelectedValue(selector));
137            }
138        }
139
140        private static void SelectorSelectionChanged(object sender, SelectionChangedEventArgs e)
141        {
142            Selector selector = (Selector)sender;
143            object selectedItem = selector.SelectedItem;
144            string selectedValuePath = GetSelectedValuePath(selector);
145            object selectedValue = GetValueForPath(selectedItem, selectedValuePath);
146            if (!Equals(GetSelectedValue(selector), selectedValue))
147            {
148                SetSelectedValue(selector, selectedValue);
149            }
150        }
151
152        private static object GetValueForPath(object instance, string valuePath)
153        {
154            MethodInfo propertyGetter = GetPropertyGetterForType(instance.GetType(), valuePath);
155            object returnValue = propertyGetter.Invoke(instance, null);
156            return returnValue;
157        }
158
159        private static MethodInfo GetPropertyGetterForType(Type type, string memberName)
160        {
161            string hashKey = string.Format(CultureInfo.InvariantCulture, "{0}&{1}", type.AssemblyQualifiedName, memberName);
162            MethodInfo methodInfo;
163            if (!cachedPropertyGetters.TryGetValue(hashKey, out methodInfo))
164            {
165                PropertyInfo property = type.GetProperty(memberName);
166                if (property != null)
167                {
168                    methodInfo = property.GetGetMethod();
169                }
170
171                cachedPropertyGetters.Add(hashKey, methodInfo);
172            }
173
174            if (methodInfo == null)
175            {
176                throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, Resources.SelectorExtensionCannotResolveMember, type.FullName, memberName, typeof(SelectorExtensions).Name));
177            }
178
179            return methodInfo;
180        }
181    }
182}