As some of you that have worked with XAML and Generics may know, there is currently no support for Generics in XAML (that is no support for direct binding of methods that use generics).
So consider this problem.
That I have various bits of static data that are used throughout the system, that are held as a collection(s) (I am using ObservableCollection<t>
) of certain data objects. The WPF UI should be able to bind to these collections, without the need for loads of get {…}
properties. Ideally, there would be a single method that returned the correct type of static data, as requested by the method. I mean there may be a lot of static data, and sure we could do this by exposing lots of properties over the static data collections, but that somehow seems old fashioned to me. We could of course also just take an Object
and return a ObservableCollection
, but that to seemed wrong, not enough typing more my liking there. The problem seems to warrant a more generic solution. Wait, doesn’t .NET support generics. Hell yeah, ok cool. So possibly we should be trying for something like the following after all:
1: public ObservableCollection<T> GetForType<T>()
2: {
3: return (ObservableCollection<T>)
typeToCollectionLookup[typeof(T)];
4: }
This seems to fit the problem domain, but how could we do this in XAML.
Unfortunately, I do not know a way of dealing with generic method calls in XAML. I know there is an ObjectDataProvider
object which has a Method
property and allows parameters to be built up in XAML. But, this would mean we would need one new ObjectDataProvider
with parameters for each type we intended to use the above method for. That’s pretty poor. There must be a better way, surely.
Shown below is an attached property that could be used with a ComboBox
control to bind to which will populate the ComboBox.ItemsSource
property by calling the generic method.
So all you have to do in the XAML is:
1: <Window x:Class="GenericBinding.Window1″
2: xmlns="http:
3: xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
4: xmlns:local="clr-namespace:GenericBinding;assembly="
5: Title="Window1″ Height="300″ Width="300″>
6: <Grid>
7:
8:
9: <ComboBox
10: local:ComboBoxProps.BoundCollectionType="local:Person"/>
11:
12:
13: </Grid>
14: </Window>
Where the ComboBoxProps.BoundCollectionTypeProperty
attached property is declared liked this:
1: using System;
2: using System.Collections.Generic;
3: using System.Linq;
4: using System.Text;
5: using System.Windows;
6: using System.Windows.Controls;
7: using System.Reflection;
8: using System.Collections;
9:
10: namespace GenericBinding
11: {
12: 13: 14: 15: 16: 17: 18: 19: public class ComboBoxProps
20: {
21: #region BoundCollectionType
22:
23: 24: 25: 26: public static readonly DependencyProperty
BoundCollectionTypeProperty =
27: DependencyProperty.RegisterAttached(
"BoundCollectionType",
28: typeof(Type), typeof(ComboBoxProps),
29: new FrameworkPropertyMetadata(null,
30: new PropertyChangedCallback(
OnBoundCollectionTypeChanged)));
31:
32: 33: 34: 35: public static Type GetBoundCollectionType
(DependencyObject d)
36: {
37: return (Type)d.GetValue(
BoundCollectionTypeProperty);
38: }
39:
40: 41: 42: 43: public static void SetBoundCollectionType(
DependencyObject d, Type value)
44: {
45: d.SetValue(BoundCollectionTypeProperty, value);
46: }
47:
48: 49: 50: 51: 52: 53: 54: 55: 56: private static void OnBoundCollectionTypeChanged(DependencyObject d,
57: DependencyPropertyChangedEventArgs e)
58: {
59: ComboBox cbSource = d as ComboBox;
60: Type t = (Type)e.NewValue;
61: Type[] types = new Type[] { t };
62:
63: MethodInfo[] methods =
64: typeof(StaticData).GetMethods(BindingFlags.Public |
BindingFlags.Instance);
65:
66: foreach (MethodInfo method in methods)
67: {
68:
69:
70:
71: ItemsSourceLookUpMethodAttribute[] attribs =
72: (ItemsSourceLookUpMethodAttribute[])
73: method.GetCustomAttributes(
74: typeof(ItemsSourceLookUpMethodAttribute), true);
75:
76:
77: if (attribs.Length > 0)
78: {
79:
80: MethodInfo genericMethod = method.MakeGenericMethod(types);
81: cbSource.ItemsSource =
82: (IEnumerable)genericMethod.Invoke(StaticData.Instance,
83: BindingFlags.Instance, null, null, null);
84: }
85: }
86: }
87: #endregion
88: }
89: }
If you want to see a full example, you can read more about this on the Codeproject article link shown below:
Hope this helps you as much as it did me. Enjoy!