wake-up-neo.com

An eine Methode in WPF binden?

Wie binden Sie eine Objektmethode in diesem Szenario 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>

Hier möchte ich an die GetChildren-Methode auf jeder RootObject des Baums binden.

EDITDas Binden an eine ObjectDataProvider scheint nicht zu funktionieren, da ich mich an eine Liste von Elementen binde und die ObjectDataProvider entweder eine statische Methode benötigt oder ihre eigene Instanz erstellt und diese verwendet.

Zum Beispiel bekomme ich mit Matts Antwort:

System.Windows.Data Fehler: 33: ObjectDataProvider kann kein Objekt erstellen. Typ = 'RootObject'; Fehler = 'Falsche Parameter für Konstruktor.'

System.Windows.Data Fehler: 34: ObjectDataProvider: Fehler beim Versuch, die Methode des Typs aufzurufen. Methode = 'GetChildren'; Typ = 'RootObject'; Fehler = 'Das angegebene Mitglied kann nicht auf dem Ziel aufgerufen werden.' TargetException: 'System.Reflection.TargetException: Nicht statische Methode erfordert ein Ziel.

86

Ein anderer Ansatz, der für Sie möglicherweise nützlich ist, ist das Erstellen einer benutzerdefinierten IValueConverter , die einen Methodennamen als Parameter verwendet, sodass er folgendermaßen verwendet wird:

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

Dieser Konverter würde die Methode unter Verwendung von Reflektion finden und aufrufen. Dies erfordert, dass die Methode keine Argumente hat.

Hier ist ein Beispiel für einen solchen Konverter:

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

Und einen entsprechenden 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));
}

Beachten Sie, dass dieser Konverter den Parameter targetType nicht erzwingt.

63
Drew Noakes

Sie sind nicht sicher, wie gut es in Ihrem Szenario funktionieren wird. Sie können jedoch die Eigenschaft MethodName von ObjectDataProvider verwenden, um eine bestimmte Methode (mit bestimmten Parametern, wenn Sie die Eigenschaft MethodParameters verwenden) zum Abrufen der Daten aufrufen zu lassen.

Hier ist ein Ausschnitt direkt von der MSDN-Seite:

<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>

Das ist also ein ObjectDataProvider, der eine "ConvertTemp" -Methode für eine Instanz einer "TemperatureScale" -Klasse aufruft und zwei Parameter (0 und TempType.Celsius) übergibt.

23
Matt Hamilton

Müssen Sie an die Methode binden?

Können Sie sich an eine Eigenschaft binden, deren Methode der Getter ist?

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

Wenn Sie keine Eigenschaft hinzufügen können, um die Methode aufzurufen (oder eine Wrapper-Klasse erstellen, die diese Eigenschaft hinzufügt), kenne ich nur einen ValueConverter.

4
Nir

ObjectDataProvider verfügt auch über eine ObjectInstance-Eigenschaft, die anstelle von ObjectType verwendet werden kann

3
Graham Ambrose

Sie können System.ComponentModel verwenden, um Eigenschaften für einen Typ dynamisch zu definieren (sie sind nicht Teil der kompilierten Metadaten). Ich habe diesen Ansatz in WPF verwendet, um das Binden an einen Typ zu ermöglichen, der seine Werte in Feldern speichert, da das Binden an Felder nicht möglich ist.

Die Typen ICustomTypeDescriptor und TypeDescriptionProvider ermöglichen es Ihnen möglicherweise, das zu erreichen, was Sie möchten. Nach diesem Artikel :

Mit TypeDescriptionProvider können Sie eine separate Klasse schreiben, die ICustomTypeDescriptor implementiert, und diese Klasse dann als Beschreibungsanbieter für andere Typen registrieren.

Ich habe diesen Ansatz nicht selbst ausprobiert, aber ich hoffe, es ist hilfreich für Sie.

3
Drew Noakes

Um an die Methode eines Objekts in Ihrem WPF-Szenario zu binden, können Sie an eine Eigenschaft binden, die einen Delegaten zurückgibt.

0
Austin_Anderson