1: /// <summary>
2: /// Class that implements a workaround for a Silverlight XAML parser
3: /// limitation that prevents the following syntax from working:
4: /// <Setter Property="IsSelected" Value="{Binding IsSelected}"/>
5: /// </summary>
6: public class SetterValueBindingHelper
7: {
8: /// <summary>
9: /// Optional type parameter used to specify the type of an attached
10: /// DependencyProperty as an assembly-qualified name, full name, or
11: /// short name.
12: /// </summary>
13: [SuppressMessage("Microsoft.Naming", "CA1721:PropertyNamesShouldNotMatchGetMethods",
14: Justification = "Unambiguous in XAML.")]
15: public string Type { get; set; }
16:
17: /// <summary>
18: /// Property name for the normal/attached DependencyProperty on which
19: /// to set the Binding.
20: /// </summary>
21: public string Property { get; set; }
22:
23: /// <summary>
24: /// Binding to set on the specified property.
25: /// </summary>
26: public Binding Binding { get; set; }
27:
28: /// <summary>
29: /// Gets the value of the PropertyBinding attached DependencyProperty.
30: /// </summary>
31: /// <param name="element">Element for which to get the property.</param>
32: /// <returns>Value of PropertyBinding attached DependencyProperty.</returns>
33: [SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters",
34: Justification = "SetBinding is only available on FrameworkElement.")]
35: public static SetterValueBindingHelper GetPropertyBinding(FrameworkElement element)
36: {
37: if (null == element)
38: {
39: throw new ArgumentNullException("element");
40: }
41: return (SetterValueBindingHelper)element.GetValue(PropertyBindingProperty);
42: }
43:
44: /// <summary>
45: /// Sets the value of the PropertyBinding attached DependencyProperty.
46: /// </summary>
47: /// <param name="element">Element on which to set the property.</param>
48: /// <param name="value">Value forPropertyBinding attached DependencyProperty.</param>
49: [SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters",
50: Justification = "SetBinding is only available on FrameworkElement.")]
51: public static void SetPropertyBinding(FrameworkElement element, SetterValueBindingHelper value)
52: {
53: if (null == element)
54: {
55: throw new ArgumentNullException("element");
56: }
57: element.SetValue(PropertyBindingProperty, value);
58: }
59:
60: /// <summary>
61: /// PropertyBinding attached DependencyProperty.
62: /// </summary>
63: public static readonly DependencyProperty PropertyBindingProperty =
64: DependencyProperty.RegisterAttached(
65: "PropertyBinding",
66: typeof(SetterValueBindingHelper),
67: typeof(SetterValueBindingHelper),
68: new PropertyMetadata(null, OnPropertyBindingPropertyChanged));
69:
70: /// <summary>
71: /// Change handler for the PropertyBinding attached DependencyProperty.
72: /// </summary>
73: /// <param name="d">Object on which the property was changed.</param>
74: /// <param name="e">Property change arguments.</param>
75: private static void OnPropertyBindingPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
76: {
77: // Get/validate parameters
78: var element = (FrameworkElement)d;
79: var item = (SetterValueBindingHelper)(e.NewValue);
80: if ((null == item.Property) || (null == item.Binding))
81: {
82: throw new ArgumentException(
83: "SetterValueBindingHelper's Property and Binding must both be set to non-null values.");
84: }
85:
86: // Get the type on which to set the Binding
87: Type type = null;
88: if (null == item.Type)
89: {
90: // No type specified; setting for the specified element
91: type = element.GetType();
92: }
93: else
94: {
95: // Try to get the type from the type system
96: type = System.Type.GetType(item.Type);
97: if (null == type)
98: {
99: // Search for the type in the list of assemblies
100: foreach (var assembly in AssembliesToSearch)
101: {
102: // Match on short or full name
103: type = assembly.GetTypes()
104: .Where(t => (t.FullName == item.Type) || (t.Name == item.Type))
105: .FirstOrDefault();
106: if (null != type)
107: {
108: // Found; done searching
109: break;
110: }
111: }
112: if (null == type)
113: {
114: // Unable to find the requested type anywhere
115: throw new ArgumentException(string.Format(CultureInfo.CurrentCulture,
116: "Unable to access type \"{0}\". Try using an assembly qualified type name.",
117: item.Type));
118: }
119: }
120: }
121:
122: // Get the DependencyProperty for which to set the Binding
123: DependencyProperty property = null;
124: var field = type.GetField(item.Property + "Property",
125: BindingFlags.FlattenHierarchy | BindingFlags.Public | BindingFlags.Static);
126: if (null != field)
127: {
128: property = field.GetValue(null) as DependencyProperty;
129: }
130: if (null == property)
131: {
132: // Unable to find the requested property
133: throw new ArgumentException(string.Format(CultureInfo.CurrentCulture,
134: "Unable to access DependencyProperty \"{0}\" on type \"{1}\".",
135: item.Property, type.Name));
136: }
137:
138: // Set the specified Binding on the specified property
139: element.SetBinding(property, item.Binding);
140: }
141:
142: /// <summary>
143: /// Returns a stream of assemblies to search for the provided type name.
144: /// </summary>
145: private static IEnumerable<Assembly> AssembliesToSearch
146: {
147: get
148: {
149: // Start with the System.Windows assembly (home of all core controls)
150: yield return typeof(Control).Assembly;
151:
152: // Fall back by trying each of the assemblies in the Deployment's Parts list
153: foreach (var part in Deployment.Current.Parts)
154: {
155: var streamResourceInfo = Application.GetResourceStream(
156: new Uri(part.Source, UriKind.Relative));
157: using (var stream = streamResourceInfo.Stream)
158: {
159: yield return part.Load(stream);
160: }
161: }
162: }
163: }
164: }