Привязать к методу в 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>

Здесь я хочу привязать метод GetChildren к каждому RootObject дерева.

EDIT Связывание с ObjectDataProvider не работает, потому что я привязываюсь к списку элементов, а ObjectDataProvider нужен либо статический метод, либо он создает его собственный экземпляр и использует это.

Например, используя ответ Matt, я получаю:

Ошибка System.Windows.Data: 33: ObjectDataProvider не может создать объект; Тип = 'RootObject'; Ошибка = 'Неверные параметры для конструктора.'

Ошибка System.Windows.Data: 34: ObjectDataProvider: ошибка при попытке вызвать метод по типу; Метод = 'GetChildren'; Тип = 'RootObject'; Ошибка = 'Указанный член не может быть вызван в цель.' TargetException: 'System.Reflection.TargetException: для нестатического метода требуется цель.

Ответ 1

Другим подходом, который может сработать для вас, является создание пользовательского 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.");
    }
}

И соответствующий 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));
}

Обратите внимание, что этот преобразователь не применяет параметр targetType.

Ответ 2

Не уверен, насколько хорошо он будет работать в вашем сценарии, но вы можете использовать свойство MethodName объекта ObjectDataProvider, чтобы он вызывал конкретную метод (с конкретными параметрами, если вы используете свойство 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, вызывающий метод "ConvertTemp" для экземпляра класса "TemperatureScale", передавая два параметра (0 и TempType.Celsius).

Ответ 3

Вам нужно привязать к методу?

Можно ли привязать свойство, которое является методом getter?

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

Ответ 4

Если вы не можете добавить свойство для вызова метода (или создать класс-оболочку, который добавляет это свойство), единственный способ, которым я знаю, - использовать ValueConverter.

Ответ 5

ObjectDataProvider также имеет свойство ObjectInstance, которое можно использовать вместо ObjectType

Ответ 6

Вы можете использовать System.ComponentModel для динамического определения свойств для типа (они не являются частью скомпилированных метаданных). Я использовал этот подход в WPF, чтобы включить привязку к типу, который сохранил его значения в полях, поскольку привязка к полям невозможна.

ICustomTypeDescriptor и TypeDescriptionProvider типы могут позволить вам достичь того, чего вы хотите. Согласно в этой статье:

TypeDescriptionProvider позволяет вам написать отдельный класс, который реализует ICustomTypeDescriptor, а затем зарегистрировать этот класс в качестве поставщика описаний для других типов.

Я сам не пробовал этот подход, но надеюсь, что это будет полезно в вашем случае.

Ответ 7

Чтобы привязать объектный метод в вашем сценарии WPF, вы можете привязываться к свойству, которое возвращает делегат.