Powiązać z metodą w WPF?


90

Jak utworzyć powiązanie z metodą obiektów w tym scenariuszu w 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>

Tutaj chcę powiązać się z GetChildrenmetodą na każdym RootObjectdrzewie.

EDYCJAObjectDataProvider Wydaje się, że powiązanie z elementem an nie działa, ponieważ wiążę się z listą elementów i ObjectDataProviderpotrzebuje albo statycznej metody, albo tworzy własną instancję i używa jej.

Na przykład, używając odpowiedzi Matta, otrzymuję:

Błąd System.Windows.Data: 33: ObjectDataProvider nie może utworzyć obiektu; Type = 'RootObject'; Błąd = „Nieprawidłowe parametry konstruktora”.

Błąd System.Windows.Data: 34: ObjectDataProvider: Błąd podczas próby wywołania metody dla typu; Method = 'GetChildren'; Type = 'RootObject'; Error = „Określony członek nie może zostać wywołany w miejscu docelowym.” TargetException: „System.Reflection.TargetException: metoda niestatyczna wymaga celu.


Tak, masz rację. ObjectDataProvider ma właściwość ObjectInstance (aby wywołać swoją metodę w określonej instancji), ale nie sądzę, że jest to właściwość zależności, więc nie można jej powiązać (AFAIK).
— Matt Hamilton

1
Tak, próbowałem powiązać z ObjectInstance i odkryłem, że nie jest to właściwość zależności.
— Cameron MacFarland

W każdym razie zostawię tam swoją odpowiedź, zarówno po to, aby nadać twojej aktualizacji trochę kontekstu, jak i po to, aby pomóc komuś, kto znajdzie to pytanie z podobnym problemem.
— Matt Hamilton

Czy faktycznie potrzebujesz powiązać z ObjectInstance? (Czy to się zmieni) Zakładając, że możesz zamiast tego utworzyć własną obsługę zdarzenia zmiany i zaktualizować ObjectDataProvider w kodzie ...
— Tim Lovell-Smith

1
Właśnie zaktualizowałem moją odpowiedź jakimś kodem źródłowym rok po fakcie.
— Drew Noakes

Odpowiedzi:


71

Innym podejściem, które może zadziałać, jest utworzenie niestandardowego, IValueConverterktóry przyjmuje nazwę metody jako parametr, tak aby był używany w następujący sposób:

ItemsSource="{Binding 
    Converter={StaticResource MethodToValueConverter},
    ConverterParameter='GetChildren'}"

Ten konwerter znalazłby i wywołałby metodę przy użyciu odbicia. To wymaga, aby metoda nie miała żadnych argumentów.

Oto przykład źródła takiego konwertera:

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.");
    }
}

I odpowiedni test jednostkowy:

[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));
}

Zauważ, że ten konwerter nie wymusza tego targetTypeparametru.


6
Hmmm, ... wygląda na włamanie, ale zaczynam myśleć, że to może być jedyny sposób. Cholera, będzie najłatwiej!
— EightyOne Unite

25

Nie jestem pewien, jak dobrze zadziała w Twoim scenariuszu, ale możesz użyć tej MethodNamewłaściwości, ObjectDataProvideraby wywołać określoną metodę (z określonymi parametrami MethodParameterswłaściwości) w celu pobrania danych.

Oto fragment pobrany bezpośrednio ze strony 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>

Więc to ObjectDataProviderjest wywołanie ConvertTempmetody na instancji TemperatureScaleklasy, przekazując dwa parametry ( 0i TempType.Celsius).


11

Czy musisz wiązać się z metodą?

Czy możesz powiązać się z właściwością, która pobiera metodę?

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

2
Rozumiem, że komentarz Camerona oznacza, że ​​wiąże się on z typem, do którego nie może dodać właściwości.
— Drew Noakes

2
Należy unikać tworzenia powiązań z właściwościami, które wywołują metody esp, jeśli metoda może być potencjalnie długotrwała. Posiadanie takich metod nie jest dobrym projektem, ponieważ konsument kodu oczekuje, że właściwość będzie miała dostęp tylko do zmiennej lokalnej.
— markmnl

@markmnl, więc jaki jest sens bezpośredniego powiązania z funkcją? Więc pytanie OP nie ma sensu w twoim przypadku?
— Teoman shipahi

4

O ile nie możesz dodać właściwości, aby wywołać metodę (lub utworzyć klasę opakowania, która dodaje tę właściwość), jedynym znanym mi sposobem jest użycie ValueConverter.


3

ObjectDataProvider ma również właściwość ObjectInstance, której można użyć zamiast ObjectType


3

Możesz użyć System.ComponentModeldo dynamicznego definiowania właściwości dla typu (nie są one częścią skompilowanych metadanych). Użyłem tego podejścia w WPF, aby włączyć powiązanie z typem, który przechowywał swoje wartości w polach, ponieważ powiązanie z polami nie jest możliwe.

Te ICustomTypeDescriptori TypeDescriptionProvidertypy mogą pozwala osiągnąć to, co chcesz. Zgodnie z tym artykułem :

TypeDescriptionProviderumożliwia napisanie oddzielnej klasy, która implementuje, ICustomTypeDescriptora następnie zarejestrować tę klasę jako dostawcę opisów dla innych typów.

Sam nie próbowałem tego podejścia, ale mam nadzieję, że będzie to pomocne w Twoim przypadku.


0

Aby powiązać z metodą obiektu w scenariuszu WPF, można powiązać z właściwością, która zwraca delegata.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.