65.9K
CodeProject 正在变化。 阅读更多。
Home

如何绑定到 XAML 中的泛型方法

emptyStarIconemptyStarIconemptyStarIconemptyStarIconemptyStarIcon

0/5 (0投票)

2009年6月17日

CPOL

2分钟阅读

viewsIcon

20169

如何在 XAML 中绑定到泛型方法。

正如一些使用过 XAML 和泛型的人可能知道的,XAML 目前不支持泛型(即不支持直接绑定使用泛型的函数)。

所以考虑一下这个问题。

我有一些在整个系统中使用的各种静态数据,这些数据存储在集合中(我使用的是 ObservableCollection<t> 类型的特定数据对象集合)。WPF UI 应该能够绑定到这些集合,而无需编写大量的 get {…} 属性。理想情况下,应该有一个方法返回请求的静态数据的正确类型。我的意思是,可能有很多静态数据,当然我们可以通过在静态数据集合上公开大量的属性来实现,但这在我看来有点过时了。我们也可以简单地接收一个 Object 并返回一个 ObservableCollection,但这似乎也不对,不够类型安全,不太符合我的喜好。这个问题似乎需要一个更通用的解决方案。等等,.NET 不支持泛型吗?当然支持,太好了。所以也许我们应该尝试以下方法:

   1:  public ObservableCollection<T> GetForType<T>()
   2:  {
   3:      return (ObservableCollection<T>)
              typeToCollectionLookup[typeof(T)];
   4:  }

这似乎符合问题领域,但我们如何在 XAML 中实现它呢?

不幸的是,我不知道如何在 XAML 中处理泛型方法调用。我知道有一个 ObjectDataProvider 对象,它有一个 Method 属性,允许在 XAML 中构建参数。但是,这意味着我们需要为我们打算使用上述方法的每种类型创建一个新的 ObjectDataProvider,并配置相应的参数。这很糟糕。一定有更好的方法,对吧?

下面展示了一个附加属性,它可以与 ComboBox 控件一起使用,用于绑定,它将通过调用泛型方法填充 ComboBox.ItemsSource 属性。

所以在 XAML 中你只需要做的是

   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>

其中 ComboBoxProps.BoundCollectionTypeProperty 附加属性的声明如下:

   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:  }

如果你想查看完整的示例,可以阅读 Codeproject 网站上显示的链接文章,了解更多信息

希望这能像对我一样对你有所帮助。祝你玩得开心!

© . All rights reserved.