Associare a un metodo in WPF?


90

Come ci si lega a un metodo di oggetti in questo scenario 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>

Qui voglio legarmi al GetChildrenmetodo su ciascuno RootObjectdegli alberi.

EDIT Binding a un ObjectDataProvidernon sembra il lavoro perché sto legame con un elenco di voci, e le ObjectDataProvideresigenze sia un metodo statico, o crea il proprio esempio e l'utilizza.

Ad esempio, usando la risposta di Matt ottengo:

System.Windows.Data Errore: 33: ObjectDataProvider non può creare l'oggetto; Type = 'RootObject'; Errore = "Parametri errati per il costruttore."

System.Windows.Data Errore: 34: ObjectDataProvider: errore durante il tentativo di richiamare il metodo sul tipo; Metodo = "GetChildren"; Type = 'RootObject'; Error = "Il membro specificato non può essere richiamato sulla destinazione." TargetException: 'System.Reflection.TargetException: il metodo non statico richiede una destinazione.


Si hai ragione. ObjectDataProvider ha una proprietà ObjectInstance (per chiamare il suo metodo su un'istanza specifica) ma non credo che sia una proprietà di dipendenza, quindi non puoi associarla (AFAIK).
Matt Hamilton

1
Sì, ho provato a collegarmi a ObjectInstance e ho scoperto che non è una proprietà di dipendenza.
Cameron MacFarland

Lascio comunque la mia risposta lì, sia per dare un contesto al tuo aggiornamento sia per aiutare qualcun altro che trova questa domanda con un problema abbastanza simile.
Matt Hamilton

Hai effettivamente bisogno di collegarti a ObjectInstance? (Cambierà) Supponendo che tu possa invece creare la tua gestione degli eventi di modifica e aggiornare ObjectDataProvider nel codice ...
Tim Lovell-Smith

1
Ho appena aggiornato la mia risposta con del codice sorgente, un anno dopo il fatto.
Drew Noakes

Risposte:


71

Un altro approccio che potrebbe funzionare per te è creare un custom IValueConverterche prende il nome di un metodo come parametro, in modo che venga usato in questo modo:

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

Questo convertitore troverà e invocerà il metodo usando la reflection. Ciò richiede che il metodo non abbia argomenti.

Ecco un esempio della fonte di un tale convertitore:

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

E un test unitario corrispondente:

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

Notare che questo convertitore non applica il targetTypeparametro.


6
Hmmm, ... sembra un hack ma sto iniziando a pensare che questo potrebbe essere l'unico modo. È dannatamente sicuro che sarà il più semplice!
EightyOne Unite,

25

Non sei sicuro di quanto bene funzionerà nel tuo scenario, ma puoi usare la MethodNameproprietà su ObjectDataProviderper far sì che chiami un metodo specifico (con parametri specifici della tua MethodParametersproprietà) per recuperare i suoi dati.

Ecco uno snippet preso direttamente dalla pagina 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>

Quindi si tratta ObjectDataProviderdi chiamare un ConvertTempmetodo su un'istanza di una TemperatureScaleclasse, passando due parametri ( 0e TempType.Celsius).


10

Devi legarti al metodo?

Puoi legarti a una proprietà che è getter è il metodo?

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

2
Considero il commento di Cameron per significare che è vincolante per un tipo a cui non può aggiungere una proprietà.
Drew Noakes,

2
È necessario evitare l'associazione a proprietà che chiamano i metodi esp se il metodo potrebbe essere potenzialmente a lunga esecuzione. Avere tali metodi nelle proprietà non è una buona idea, poiché un consumatore di codice si aspetta che una proprietà acceda solo a una variabile locale.
markmnl

@markmnl quindi qual è il punto di legarsi direttamente alla funzione? Quindi la domanda di OP non ha alcun senso nel tuo caso?
Teoman shipahi

4

A meno che tu non possa aggiungere una proprietà per chiamare il metodo (o creare una classe wrapper che aggiunge quella proprietà) l'unico modo che conosco è usare un ValueConverter.


3

ObjectDataProvider dispone anche di una proprietà ObjectInstance che può essere utilizzata al posto di ObjectType


3

È possibile utilizzare System.ComponentModelper definire dinamicamente le proprietà per un tipo (non fanno parte dei metadati compilati). Ho usato questo approccio in WPF per abilitare l'associazione a un tipo che memorizzava i suoi valori nei campi, poiché l'associazione ai campi non è possibile.

I tipi ICustomTypeDescriptore TypeDescriptionProviderpotrebbero consentirti di ottenere ciò che desideri. Secondo questo articolo :

TypeDescriptionProviderconsente di scrivere una classe separata che implementa ICustomTypeDescriptore quindi di registrare questa classe come fornitore di descrizioni per altri tipi.

Non ho provato questo approccio da solo, ma spero che sia utile nel tuo caso.


0

Per eseguire il binding al metodo di un oggetto nello scenario WPF, puoi eseguire il binding a una proprietà che restituisce un delegato.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.