【问题标题】:Bind to a method in WPF?绑定到 WPF 中的方法?
【发布时间】:2010-10-04 20:58:03
【问题描述】:

在这种情况下,如何在 WPF 中绑定到对象方法?

public class RootObject
{
    public string Name { get; }

    public ObservableCollection<ChildObject> GetChildren() {...}
}

public class ChildObject
{
    public string Name { get; }
}

XAML:

<TreeView ItemsSource="some list of RootObjects">
    <TreeView.Resources>
        <HierarchicalDataTemplate DataType="{x:Type data:RootObject}" 
                                  ItemsSource="???">
            <TextBlock Text="{Binding Path=Name}" />
        </HierarchicalDataTemplate>
        <HierarchicalDataTemplate DataType="{x:Type data:ChildObject}">
            <TextBlock Text="{Binding Path=Name}" />
        </HierarchicalDataTemplate>
    </TreeView.Resources>
</TreeView>

这里我想在树的每个RootObject 上绑定GetChildren 方法。

EDIT 绑定到 ObjectDataProvider 似乎不起作用,因为我正在绑定到项目列表,而 ObjectDataProvider 需要静态方法,或者它创建自己的方法实例并使用它。

例如,使用马特的答案,我得到:

System.Windows.Data 错误:33:ObjectDataProvider 无法创建对象;类型='Ro​​otObject'; Error='构造函数的参数错误。'

System.Windows.Data 错误:34:ObjectDataProvider:尝试调用类型上的方法失败;方法='GetChildren';类型='Ro​​otObject'; 'Error='指定的成员不能在目标上调用。' TargetException:'System.Reflection.TargetException: 非静态方法需要一个目标。

【问题讨论】:

  • 是的,你是对的。 ObjectDataProvider 确实有一个 ObjectInstance 属性(在特定实例上调用它的方法),但我不认为它是一个依赖属性,所以你不能绑定它(AFAIK)。
  • 是的,我尝试绑定到 ObjectInstance,发现它不是依赖属性。
  • 无论如何我都会把我的答案留在那里,既可以为您的更新提供一些背景信息,也可以帮助其他发现此问题的人遇到足够类似的问题。
  • 你真的需要绑定到ObjectInstance吗? (它会改变)假设您可以创建自己的更改事件处理并在代码中更新 ObjectDataProvider...
  • 刚刚用一些源代码更新了我的答案,一年后。

标签: .net wpf data-binding xaml


【解决方案1】:

另一种可能对您有用的方法是创建一个自定义的IValueConverter,它将方法名称作为参数,以便像这样使用它:

ItemsSource="{Binding 
    Converter={StaticResource MethodToValueConverter},
    ConverterParameter='GetChildren'}"

此转换器将使用反射查找并调用该方法。这要求方法没有任何参数。

以下是此类转换器来源的示例:

public sealed class MethodToValueConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        var methodName = parameter as string;
        if (value==null || methodName==null)
            return value;
        var methodInfo = value.GetType().GetMethod(methodName, new Type[0]);
        if (methodInfo==null)
            return value;
        return methodInfo.Invoke(value, new object[0]);
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotSupportedException("MethodToValueConverter can only be used for one way conversion.");
    }
}

以及相应的单元测试:

[Test]
public void Convert()
{
    var converter = new MethodToValueConverter();
    Assert.AreEqual("1234", converter.Convert(1234, typeof(string), "ToString", null));
    Assert.AreEqual("ABCD", converter.Convert(" ABCD ", typeof(string), "Trim", null));

    Assert.IsNull(converter.Convert(null, typeof(string), "ToString", null));

    Assert.AreEqual("Pineapple", converter.Convert("Pineapple", typeof(string), "InvalidMethodName", null));
}

请注意,此转换器不强制使用 targetType 参数。

【讨论】:

  • 嗯,...似乎是一个黑客,但我开始认为这可能是唯一的方法。这该死的肯定会是最简单的!
【解决方案2】:

不确定它在您的场景中效果如何,但您可以使用ObjectDataProvider 上的MethodName 属性让它调用特定方法(带有您MethodParameters 属性的特定参数)来检索其数据。

这是直接取自 MSDN 页面的 sn-p:

<Window.Resources>
    <ObjectDataProvider ObjectType="{x:Type local:TemperatureScale}"
        MethodName="ConvertTemp" x:Key="convertTemp">
        <ObjectDataProvider.MethodParameters>
            <system:Double>0</system:Double>
            <local:TempType>Celsius</local:TempType>
        </ObjectDataProvider.MethodParameters>
    </ObjectDataProvider>
</Window.Resources>

所以这是一个ObjectDataProvider,它在TemperatureScale 类的实例上调用ConvertTemp 方法,传递两个参数(0TempType.Celsius)。

【讨论】:

    【解决方案3】:

    你必须绑定到方法吗?

    你能绑定到getter是方法的属性吗?

    public ObservableCollection<ChildObject> Children
    {
       get
       {
          return GetChildren();
       }
    }
    

    【讨论】:

    • 我认为 Cameron 的评论意味着他正在绑定到一个他无法添加属性的类型。
    • 你应该避免绑定到调用方法的属性,尤其是如果方法可能长时间运行。拥有它属性的这些方法并不是一个好的设计,因为代码的使用者期望一个属性只能访问一个局部变量。
    • @markmnl 那么直接绑定到函数有什么意义呢?所以OP的问题对你的情况没有任何意义?
    【解决方案4】:

    除非您可以添加一个属性来调用该方法(或创建一个添加该属性的包装类),否则我知道的唯一方法是使用 ValueConverter。

    【讨论】:

      【解决方案5】:

      ObjectDataProvider 也有一个 ObjectInstance 属性,可以用来代替 ObjectType

      【讨论】:

        【解决方案6】:

        您可以使用System.ComponentModel 动态定义类型的属性(它们不是已编译元数据的一部分)。我在 WPF 中使用了这种方法来启用绑定到将其值存储在字段中的类型,因为无法绑定到字段。

        ICustomTypeDescriptorTypeDescriptionProvider 类型可以让您实现您想要的。根据this article

        TypeDescriptionProvider 允许您编写一个单独的类来实现ICustomTypeDescriptor,然后将该类注册为其他类型的描述提供者。

        我自己没有尝试过这种方法,但我希望它对你的情况有所帮助。

        【讨论】:

          【解决方案7】:

          要在 WPF 场景中绑定到对象的方法,您可以绑定到返回委托的属性。

          【讨论】:

            猜你喜欢
            • 2011-08-16
            • 2012-12-06
            • 1970-01-01
            • 2013-08-31
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2011-02-02
            • 1970-01-01
            相关资源
            最近更新 更多