Bind to a method in WPF?
.NetWpfData BindingXaml.Net Problem Overview
How do you bind to an objects method in this scenario in 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>
Here I want to bind to the GetChildren
method on each RootObject
of the tree.
EDIT Binding to an ObjectDataProvider
doesn't seem to work because I'm binding to a list of items, and the ObjectDataProvider
needs either a static method, or it creates it's own instance and uses that.
For example, using Matt's answer I get:
> System.Windows.Data Error: 33 : ObjectDataProvider cannot create object; Type='RootObject'; Error='Wrong parameters for constructor.' > > System.Windows.Data Error: 34 : ObjectDataProvider: Failure trying to invoke method on type; Method='GetChildren'; Type='RootObject'; Error='The specified member cannot be invoked on target.' TargetException:'System.Reflection.TargetException: Non-static method requires a target.
.Net Solutions
Solution 1 - .Net
Another approach that might work for you is to create a custom IValueConverter
that takes a method name as a parameter, so that it would be used like this:
ItemsSource="{Binding
Converter={StaticResource MethodToValueConverter},
ConverterParameter='GetChildren'}"
This converter would find and invoke the method using reflection. This requires the method to not have any arguments.
Here's an example of such a converter's source:
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.");
}
}
And a corresponding unit test:
[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));
}
Note that this converter does not enforce the targetType
parameter.
Solution 2 - .Net
Not sure how well it will work in your scenario, but you can use the MethodName
property on ObjectDataProvider
to have it call a specific method (with specific parameters of you MethodParameters
property) to retrieve its data.
Here's a snippet taken directly from the MSDN page:
<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>
So that's an ObjectDataProvider
that's calling a ConvertTemp
method on an instance of a TemperatureScale
class, passing two parameters (0
and TempType.Celsius
).
Solution 3 - .Net
Do you have to bind to the method?
Can you bind to a property who's getter is the method?
public ObservableCollection<ChildObject> Children
{
get
{
return GetChildren();
}
}
Solution 4 - .Net
Unless you can add a property to call the method (or create a wrapper class that adds that property) the only way I know of is using a ValueConverter.
Solution 5 - .Net
ObjectDataProvider also has an ObjectInstance property that can be used instead of ObjectType
Solution 6 - .Net
You can use System.ComponentModel
to define properties for a type dynamically (they're not part of the compiled metadata). I used this approach in WPF to enable binding to a type that stored its values in fields, as binding to fields is not possible.
The ICustomTypeDescriptor
and TypeDescriptionProvider
types might allow you to achieve what you want. According to this article:
> TypeDescriptionProvider
allows you to write a separate class that implements ICustomTypeDescriptor
and then to register this class as the provider of descriptions for other types.
I haven't tried this approach myself, but I hope it's helpful in your case.
Solution 7 - .Net
To bind to an object's method in your WPF scenario, you can bind to a property that returns a delegate.