[英]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
方法,传递两个参数( 0
和TempType.Celsius
)。
你必须绑定到方法吗?
你能绑定到一个属性上,getter 是方法吗?
public ObservableCollection<ChildObject> Children
{
get
{
return GetChildren();
}
}
除非您可以添加一个属性来调用该方法(或创建一个添加该属性的包装类),否则我所知道的唯一方法是使用 ValueConverter。
ObjectDataProvider 也有一个 ObjectInstance 属性,可以用来代替 ObjectType
您可以使用System.ComponentModel
动态定义类型的属性(它们不是编译元数据的一部分)。 我在 WPF 中使用这种方法来启用绑定到将其值存储在字段中的类型,因为绑定到字段是不可能的。
ICustomTypeDescriptor
和TypeDescriptionProvider
类型可能允许您实现您想要的。 根据这篇文章:
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.