XAML 绑定到WPF中的方法?

sq1bmfud  于 2022-12-07  发布在  其他
关注(0)|答案(8)|浏览(154)

在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需要一个静态方法,或者它创建自己的示例并使用该方法。

例如,使用Matt的答案,我得到:
系统.窗口.数据错误:33:ObjectDataProvider无法创建对象;类型=“根对象”;错误='构造函数的参数错误。'
系统.窗口.数据错误:34:对象数据提供程序:尝试对类型调用方法时失败;方法=“获取子项”;类型=“根对象”;错误='无法在目标上调用指定的成员。' TargetException:'系统。反射。TargetException:非静态方法需要目标。

aiqt4smr

aiqt4smr1#

另一种可能适用于您的方法是创建一个自定义的IValueConverter,该IValueConverter将方法名作为参数,因此它的使用方式如下:

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

这个转换子会使用反映(Reflection)来寻找并叫用方法。这要求方法没有任何参数。
下面是这样一个转换器的源代码示例:

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参数。

fnx2tebb

fnx2tebb2#

不确定它在您的场景中的工作效果如何,但是您可以使用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)。

1szpjjfi

1szpjjfi3#

是否必须绑定到方法?
你能绑定到一个getter是方法的属性吗?

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

zi8p0yeb4#

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

gywdnpxw

gywdnpxw5#

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

acruukt9

acruukt96#

您可以使用System.ComponentModel动态定义类型的属性(它们不是编译的元数据的一部分)。我在WPF中使用了这种方法来支持绑定到将其值存储在字段中的类型,因为绑定到字段是不可能的。
ICustomTypeDescriptorTypeDescriptionProvider类型可能允许您实现您想要的结果。根据this article
TypeDescriptionProvider可让您撰写实作ICustomTypeDescriptor的个别类别,然后将这个类别注册为其他型别之描述的提供者。
我自己还没有尝试过这种方法,但我希望它对你的情况有所帮助。

0tdrvxhp

0tdrvxhp7#

若要系结至WPF案例中的对象方法,您可以系结至传回委派的属性。

t3irkdon

t3irkdon8#

与Drew Noakes的answer相同,但具有使用扩展方法的能力。

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.");
    }
}

相关问题