web-dev-qa-db-fra.com

Existe-t-il un moyen d'utiliser l'héritage de modèles de données dans WPF?

Est-il possible d'avoir DataTemplate composition ou héritage (similaire à "BasedOn" dans les styles)? Il y a 2 cas où j'en ai besoin.

  1. Pour les classes héritées: j'ai une classe de base avec plusieurs classes héritées. Je ne veux pas dupliquer le modèle de classe de base dans chacun des DataTemplate de la classe dérivée.

  2. Différentes vues: pour la même classe, je veux définir un modèle de données, puis ajouter à ce modèle selon les besoins. Ex. le modèle de base affichera les données dans l'objet, puis je veux différents modèles qui peuvent effectuer différentes actions sur l'objet, tout en affichant les données (héritant du modèle de base).

50
Fragilerus

La seule chose que j'ai trouvée pour ce genre de chose est la suivante:

<DataTemplate x:Key="BaseClass">
  <!-- base class template here -->
</DataTemplate>
<DataTemplate DataType="{x:Type app:BaseClass}">
  <ContentPresenter Content="{Binding}" 
                    ContentTemplate="{StaticResource BaseClass}"/>
</DataTemplate>
<DataTemplate DataType="{x:Type app:DerivedClass}">
  <StackPanel>
    <ContentPresenter Content="{Binding}" 
                      ContentTemplate="{StaticResource BaseClass}"/>
    <!-- derived class extra template here -->
  </StackPanel>
</DataTemplate>

Fondamentalement, cela crée un modèle "commun" qui peut être référencé à l'aide d'une clé (BaseClass dans ce cas). Ensuite, nous définissons le vrai DataTemplate pour la classe de base et toutes les classes dérivées. Le modèle de classe dérivé ajouterait alors ses propres "trucs".

Il y a eu une discussion à ce sujet sur msdn il y a quelque temps, mais personne n'a trouvé une meilleure solution que j'ai vue.

43
Liz

@Fragilerus et @Liz, en fait, je pense que j'ai trouvé quelque chose de mieux. Voici une autre approche qui évite non seulement la liaison ContentPresenter supplémentaire, mais supprime également la nécessité d'avoir à appliquer un modèle dans un modèle puisque le contenu partagé est un contenu direct qui est défini au moment de la compilation. La seule chose qui se produit au moment de l'exécution serait les liaisons que vous définissez dans le contenu direct. En tant que tel, cela accélère considérablement l'interface utilisateur par rapport à l'autre solution.

<!-- Content for the template (note: not a template itself) -->
<Border x:Shared="False" 
        x:Key="Foo" 
        BorderBrush="Red" 
        BorderThickness="1" 
        CornerRadius="4">
    <TextBlock Text="{Binding SomeProp}" />
</Border>

<DataTemplate x:Key="TemplateA">
    <!-- Static resource - No binding needed -->
    <ContentPresenter Content="{StaticResource Foo}" /> 
</DataTemplate>

<DataTemplate x:Key="TemplateB">
    <!-- Static resource - No binding needed -->
    <ContentPresenter Content="{StaticResource Foo}" />
</DataTemplate>

Important: assurez-vous d'utiliser le x:Shared attribut sur votre contenu partagé ou cela ne fonctionnera pas.

The WPF'y Way

Cela dit, ce n'est vraiment pas la façon la plus conviviale de WPF de faire ce que vous recherchez. Cela peut être réalisé en utilisant la classe DataTemplateSelector qui fait exactement cela ... sélectionnez un modèle de données basé sur les critères que vous définissez.

Par exemple, vous pouvez facilement en configurer un qui recherche vos types de données connus et renvoie le même DataTemplate pour les deux, mais pour tous les autres types, il revient au système de résoudre le DataTemplate. C'est ce que nous faisons réellement ici.

J'espère que cela t'aides! :)

22
MarqueIV