2009-02-02 7 views
82

Comment vous lier à une méthode d'objets dans ce scénario dans WPF?Lier à une méthode dans 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> 

Ici, je veux lier à la méthode GetChildren sur chaque RootObject de l'arbre.

EDIT à une liaison ObjectDataProvider ne semble pas fonctionner parce que je suis lier à une liste d'articles, et les besoins ObjectDataProvider soit une méthode statique, ou il crée son propre exemple et l'utilise.

Par exemple, en utilisant la réponse de Matt Je reçois:

System.Windows.Data Error: 33 : ObjectDataProvider cannot create object; Type='RootObject'; Error='Wrong parameters for constructor.'

System.Windows.Data Error: 34 : ObjectDataProvider: Failure trying to invoke method on type; Method='GetChildren'; Type='RootObject'; Error='The specified member cannot be invoked on target.' TargetException:'System.Reflection.TargetException: Non-static method requires a target.

+0

Oui, vous avez raison. ObjectDataProvider possède une propriété ObjectInstance (pour appeler sa méthode sur une instance spécifique) mais je ne pense pas qu'il s'agisse d'une propriété de dépendance, donc vous ne pouvez pas lier ce paramètre (AFAIK). –

+1

Ouais j'ai essayé de lier à ObjectInstance et j'ai découvert qu'il ne s'agissait pas d'une propriété de dépendance. –

+0

Je vais laisser ma réponse de toute façon, à la fois pour donner un peu de contexte à votre mise à jour et pour aider quelqu'un d'autre qui trouve cette question avec un problème assez similaire. –

Répondre

22

Je ne sais pas comment cela fonctionnera dans votre scénario, mais vous pouvez utiliser la propriété MethodName sur ObjectDataProvider pour avoir appeler une méthode spécifique (avec spécifique paramètres si vous la propriété MethodParameters) pour récupérer ses données.

Voici un extrait pris directement à partir de la page 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> 

C'est donc un ObjectDataProvider qui est d'appeler une méthode « ConvertTemp » sur une instance d'une classe « TemperatureScale », en passant deux paramètres (0 et TempType.Celsius) .

+0

J'ai mis à jour ma réponse en fonction de votre réponse. –

9

Devez-vous vous lier à la méthode? Pouvez-vous lier à une propriété dont la méthode getter est getter?

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

Je dois me lier à une méthode. –

+2

Je prends le commentaire de Cameron pour signifier qu'il lie à un type auquel il ne peut pas ajouter une propriété. –

+1

Vous devriez éviter de vous lier aux propriétés qui appellent les méthodes esp si la méthode peut potentiellement être longue. Avec de telles méthodes, ses propriétés ne sont pas de bonne qualité, car un consommateur de code s'attend à ce qu'une propriété accède uniquement à une variable locale. – markmnl

4

À moins que vous pouvez ajouter une propriété pour appeler la méthode (ou créer une classe wrapper qui ajoute que la propriété), la seule façon que je connaisse utilise un ValueConverter.

3

ObjectDataProvider a également une propriété ObjectInstance qui peut être utilisé au lieu de TypeObjet

3

Vous pouvez utiliser System.ComponentModel pour définir les propriétés d'un type dynamique (ils ne font pas partie des métadonnées compilées). J'ai utilisé cette approche dans WPF pour activer la liaison à un type qui stockait ses valeurs dans des champs, car la liaison aux champs n'est pas possible. Les types ICustomTypeDescriptor et TypeDescriptionProvider peuvent vous permettre d'obtenir ce que vous voulez. Selon this article:

TypeDescriptionProvider allows you to write a separate class that implements ICustomTypeDescriptor and then to register this class as the provider of descriptions for other types.

Je ne l'ai pas essayé moi-même approche, mais j'espère que ce sera utile dans votre cas.

59

Une autre approche qui pourrait fonctionner pour vous est de créer une coutume IValueConverter qui prend un nom de méthode en tant que paramètre, de sorte qu'il serait utilisé comme ceci:

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

Ce convertisseur trouverait et invoquer la méthode en utilisant la réflexion. Cela nécessite que la méthode n'ait aucun argument.

Voici un exemple de source d'un tel convertisseur:

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

Et un test unitaire correspondant:

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

Notez que ce convertisseur n'applique pas le paramètre targetType.

+4

Hmmm, ... semble être un hack mais je commence à penser que cela pourrait être le seul moyen. C'est sûr que ça va être le plus facile! – Stimul8d

0

Pour lier à la méthode d'un objet dans votre scénario WPF, vous pouvez lier à une propriété qui renvoie un délégué.

Questions connexes