Прив’язати до методу в WPF?


90

Як зв’язати метод об’єктів у цьому сценарії у 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 Прив’язка до an ObjectDataProvider, здається, не працює, оскільки я прив’язую до списку елементів і ObjectDataProviderпотребую або статичного методу, або він створює власний екземпляр і використовує це.

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

Помилка System.Windows.Data: 33: ObjectDataProvider не може створити об'єкт; Тип = 'RootObject'; Помилка = 'Неправильні параметри конструктора.'

Помилка System.Windows.Data: 34: ObjectDataProvider: Помилка спроби викликати метод типу; Метод = 'GetChildren'; Тип = 'RootObject'; Помилка = 'Зазначеного члена не можна викликати на ціль.' TargetException: 'System.Reflection.TargetException: Нестатичний метод вимагає цілі.


Так, ти маєш рацію. ObjectDataProvider має властивість ObjectInstance (для виклику його методу на конкретному екземплярі), але я не думаю, що це властивість залежності, тому ви не можете прив'язати його (AFAIK).
Метт Гамільтон, 02

1
Так, я спробував прив’язати ObjectInstance і з’ясував, що це не властивість залежності.
Cameron MacFarland 02

Я все одно залишу там свою відповідь, і для того, щоб дати вашому оновленню певний контекст, і допомогти комусь іншому, хто знайде це питання з досить подібною проблемою.
Метт Гамільтон, 02

Вам насправді потрібно прив'язувати ObjectInstance? (Чи зміниться це) Припускаючи, що ви можете замість цього створити власну обробку подій змін та оновити ObjectDataProvider у коді ...
Тім Ловелл-Сміт

1
Просто оновив свою відповідь деяким вихідним кодом через рік після того, як це сталося.
Дрю Ноукс

Відповіді:


71

Іншим підходом, який може підійти для вас, є створення користувацького, 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параметр.


6
Гммм, ... здається хак, але я починаю думати, що це може бути єдиним способом. Напевне, це буде найлегше!
EightyOne Unite

25

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


10

Чи потрібно прив'язувати метод?

Чи можете ви прив'язати до властивості, хто отримує метод?

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

2
Я вважаю коментар Кемерона таким, що він прив'язується до типу, до якого він не може додати властивість.
Дрю Ноукс

2
Слід уникати прив'язки до властивостей, які викликають методи esp, якщо метод потенційно може тривати довго. Наявність таких методів не відрізняється хорошим дизайном, оскільки споживач коду очікує, що властивість матиме доступ лише до локальної змінної.
markmnl

@markmnl, то який сенс прив'язувати безпосередньо до функції? Отже, питання OP не має сенсу у вашій справі?
Teoman shipahi

4

Якщо ви не можете додати властивість для виклику методу (або створити клас обгортки, який додає цю властивість), єдиний спосіб, який я знаю, - це використання ValueConverter.


3

ObjectDataProvider також має властивість ObjectInstance, яке можна використовувати замість ObjectType


3

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

Ці ICustomTypeDescriptorта TypeDescriptionProviderтипи можуть дозволити вам досягти того, чого ви хочете. Відповідно до цієї статті :

TypeDescriptionProviderдозволяє написати окремий клас, який реалізує, ICustomTypeDescriptorа потім зареєструвати цей клас як постачальника описів для інших типів.

Я сам не пробував такий підхід, але сподіваюся, він буде корисним у вашому випадку.


0

Для прив’язки до методу об’єкта у вашому сценарії WPF ви можете прив’язати до властивості, яка повертає делегата.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.