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.
-
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 if 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
).Cameron MacFarland : I've updated my answer based on your answer. -
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(); } }
Cameron MacFarland : I have to bind to a method.Drew Noakes : I take Cameron's comment to mean that he is binding to a type that he can not add a property to. -
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.
-
ObjectDataProvider also has an ObjectInstance property that can be used instead of ObjectType
-
Did anyone find a solution to the problem? I would greatly appreciate a sample.
Maslow : -1 this is a question and not an answer. -
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
andTypeDescriptionProvider
types might allow you to achieve what you want. According to this article:TypeDescriptionProvider
allows you to write a separate class that implementsICustomTypeDescriptor
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.
-
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.Stimul8d : Hmmm,...seems like a hack but I'm starting to think this might be the only way. It's damn sure gonna be the easiest!
0 comments:
Post a Comment