50 votes

WPF data binding à l'interface et non à l'objet réel - le casting est-il possible?

Dire que j'ai une interface comme ça :

public interface ISomeInterface
{
...
}

J'ai aussi quelques classes qui implémentent cette interface ;

public class SomeClass : ISomeInterface
{
...
}

Maintenant j'ai une ListBox WPF qui répertorie des éléments de type ISomeInterface, en utilisant un DataTemplate personnalisé.

Le moteur de liaison de données ne permet apparemment pas (à ce que j'ai pu comprendre) de se lier aux propriétés de l'interface - il voit que l'objet est un objet SomeClass, et les données n'apparaissent que si SomeClass possède la propriété liée disponible en tant que propriété non-interface.

Comment puis-je indiquer au DataTemplate de considérer que chaque objet est un ISomeInterface, et pas un SomeClass, etc.?

Merci!

62voto

dummyboy Points 336

Pour lier aux membres d'interface implémentés explicitement, vous devez simplement utiliser les parenthèses. Par exemple:

implicite:

{Binding Path=MyValue}

explicite:

{Binding Path=(mynamespacealias:IMyInterface.MyValue)}

19voto

MD.Unicorn Points 8354

Cette réponse du forum Microsoft par Beatriz Costa - MSFT vaut la peine d'être lue (plutôt ancienne) :

L'équipe de liaison de données a discuté d'ajouter la prise en charge des interfaces il y a un certain temps, mais a finalement décidé de ne pas le faire car nous n'avons pas réussi à trouver un bon design pour cela. Le problème était que les interfaces n'ont pas de hiérarchie comme le font les types objets. Considérez le scénario où votre source de données implémente à la fois IMyInterface1 et IMyInterface2 et vous avez des DataTemplates pour les deux de ces interfaces dans les ressources : quel DataTemplate pensez-vous que nous devrions choisir ?

Lorsque nous faisons du templating de données implicite pour les types objets, nous essayons d'abord de trouver un DataTemplate pour le type exact, puis pour son parent, grand-parent et ainsi de suite. Il y a un ordre très bien défini des types pour nous à appliquer. Lorsque nous avons parlé d'ajouter la prise en charge des interfaces, nous avons envisagé d'utiliser la réflexion pour trouver toutes les interfaces et les ajouter à la fin de la liste des types. Le problème auquel nous avons été confrontés était de définir l'ordre des interfaces lorsque le type implémente plusieurs interfaces.

L'autre chose que nous avons dû garder à l'esprit est que la réflexion n'est pas si bon marché, et cela diminuerait un peu nos performances pour ce scénario.

Alors quelle est la solution ? Vous ne pouvez pas le faire tout en XAML, mais vous pouvez le faire facilement avec un peu de code. La propriété ItemTemplateSelector de ItemsControl peut être utilisée pour choisir quel DataTemplate vous voulez utiliser pour chaque élément. Dans la méthode SelectTemplate de votre sélecteur de modèle, vous recevez en paramètre l'élément que vous allez template. Ici, vous pouvez vérifier pour quelle interface il implémente et renvoyer le DataTemplate qui correspond.

13voto

user7116 Points 39829

La réponse courte est que les DataTemplate ne prennent pas en charge les interfaces (pensez à l'héritage multiple, explicite. implicite, etc.). La façon dont nous avons tendance à contourner cela est d'avoir une classe de base que les éléments étendent pour permettre la spécialisation/généralisation des DataTemplate. Cela signifie qu'une solution décente, mais pas nécessairement optimale, serait :

public abstract class SomeClassBase
{

}

public class SomeClass : SomeClassBase
{

}

9voto

Pieter Breed Points 2288

Vous avez une autre option. Définissez une clé sur votre DataTemplate et référencez cette clé dans l'ItemTemplate. Comme ceci :

ensuite référencez le modèle par clé où vous souhaitez l'utiliser, comme ceci :

Rendu

8voto

MikeKulls Points 1498

La réponse suggérée par dummyboy est la meilleure réponse (elle devrait être votée en haut à mon avis). Cela a un problème que le concepteur n'aime pas (il génère une erreur "L'objet null ne peut pas être utilisé en tant que paramètre d'accession pour un Path de propriété) mais il existe une bonne solution. La solution de contournement est de définir l'élément dans un datatemplate, puis définir le modèle sur une étiquette ou un autre contrôle de contenu. Par exemple, je voulais ajouter une image comme ceci

Mais cela me donnait toujours la même erreur. La solution était de créer une étiquette et d'utiliser un modèle de données pour afficher mon contenu

Cela a ses inconvénients mais cela semble fonctionner assez bien pour moi.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X