繁体   English   中英

绑定到 WPF 中的方法?

[英]Bind to a method in WPF?

在 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方法。

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

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

System.Windows.Data 错误:33:ObjectDataProvider 无法创建 object; 类型='RootObject'; Error='构造函数的参数错误。'

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

另一种可能对您IValueConverter的方法是创建一个将方法名称作为参数的自定义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参数。

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

这是直接取自 MSDN 页面的片段:

<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 )。

你必须绑定到方法吗?

你能绑定到一个属性上,getter 是方法吗?

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

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

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

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

ICustomTypeDescriptorTypeDescriptionProvider类型可能允许您实现您想要的。 根据这篇文章

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

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

若要绑定到 WPF 方案中的对象方法,可以绑定到返回委托的属性。

与 Drew Noakes 的回答相同,但能够使用扩展方法。

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, Type.EmptyTypes);
        if (methodInfo == null) {
            methodInfo = GetExtensionMethod(value.GetType(), methodName);
            if (methodInfo == null) return value;
            return methodInfo.Invoke(null, new[] { value });
        }
        return methodInfo.Invoke(value, Array.Empty<object>());
    }

    static MethodInfo? GetExtensionMethod(Type extendedType, string methodName)
    {
        var method = Assembly.GetExecutingAssembly()
            .GetTypes()
            .Where(type => !type.IsGenericType && !type.IsNested)
            .SelectMany(type => type.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic), (_, method) => method)
            .Where(m => m.IsDefined(typeof(ExtensionAttribute), false))
            .Where(m => m.GetParameters()[0].ParameterType == extendedType)
            .FirstOrDefault(m => m.Name == methodName);
        return method;
    }

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM