How To Bind To Generic Method In XAML

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 through out the system, that are held as a collection(s) (I am using ObservableCollection) 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:  }

.csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, “Courier New”, courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }

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 a 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://schemas.microsoft.com/winfx/2006/xaml/presentation"
   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>

.csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, “Courier New”, courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }

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:      /// <summary>
  13:      /// Provides a mechanism for binding a ComboBox.ItemSource against
  14:      /// a generic method within the StaticData singleton. Where the generic
  15:      /// method has a signature as follows:
  16:      /// 
  17:      /// public ObservableCollection<T> GetForType<T>()
  18:      /// </summary>
  19:      public class ComboBoxProps
  20:      {
  21:          #region BoundCollectionType
  22:  
  23:          /// <summary>
  24:          /// BoundCollectionType Attached Dependency Property
  25:          /// </summary>
  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:          /// <summary>
  33:          /// Gets the BoundCollectionType property.  
  34:          /// </summary>
  35:          public static Type GetBoundCollectionType
                     (DependencyObject d)
  36:          {
  37:              return (Type)d.GetValue(
                       BoundCollectionTypeProperty);
  38:          }
  39:  
  40:          /// <summary>
  41:          /// Sets the BoundCollectionType property.  
  42:          /// </summary>
  43:          public static void SetBoundCollectionType(
                     DependencyObject d, Type value)
  44:          {
  45:              d.SetValue(BoundCollectionTypeProperty, value);
  46:          }
  47:  
  48:          /// <summary>
  49:          /// Handles changes to the BoundCollectionType property.
  50:          /// Uses Reflection to obtain the method within the StaticData singleton class
  51:          /// that has the generic method that we need to use to get the values from.
  52:          /// The method will be marked with a custom ItemsSourceLookUpMethodAttribute
  53:          /// to indicate which method is to be used, to create a Dynamic call to
  54:          /// using the correct generic parameter.
  55:          /// </summary>
  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:                  //Didnt like looking up MethodInfo.Name based on a string as it could
  69:                  //change, so use a custom attribute to look for on the method instead
  70:  
  71:                  ItemsSourceLookUpMethodAttribute[] attribs =
  72:                      (ItemsSourceLookUpMethodAttribute[])
  73:                          method.GetCustomAttributes(
  74:                              typeof(ItemsSourceLookUpMethodAttribute), true);
  75:  
  76:                  //is this the correct MethodInfo to invoke
  77:                  if (attribs.Length > 0)
  78:                  {
  79:                      // create the generic method
  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:  }

.csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, “Courier New”, courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }If you want to see a full example you can read more about this on the codeproject article link shown below

http://www.codeproject.com/KB/WPF/GenericXAML.aspx

Hope this helps you as much as it did me. Enjoy

Advertisements

2 thoughts on “How To Bind To Generic Method In XAML

  1. […] Sacha Barber on How To Bind To Generic Method In XAML […]

  2. […] How to Bind to Generic Method in XAML (Sacha Barber) […]

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: