Привязать к методу в WPF? - PullRequest
86 голосов
/ 02 февраля 2009

Как вы связываетесь с методом объектов в этом сценарии в 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 нужен либо статический метод, либо он создает собственный экземпляр и использует это.

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

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

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

Ответы [ 7 ]

64 голосов
/ 10 мая 2009

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

24 голосов
/ 02 февраля 2009

Не уверен, насколько хорошо он будет работать в вашем сценарии, но вы можете использовать свойство 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).

9 голосов
/ 02 февраля 2009

Обязательно ли привязывать к методу?

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

public ObservableCollection<ChildObject> Children
{
   get
   {
      return GetChildren();
   }
}
4 голосов
/ 02 февраля 2009

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

3 голосов
/ 10 мая 2009

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

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

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

Я сам не пробовал такой подход, но надеюсь, что он вам пригодится.

3 голосов
/ 02 марта 2009

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

0 голосов
/ 22 июня 2017

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...