2017-06-25 1 views
-1

J'ai un ListBox qui présente une liste d'objets par le biais de ItemSource. Étant donné que chaque objet a des besoins d'affichage spéciaux, je définis un ItemTemplateSelector qui renvoie le DataTemplate approprié en fonction de l'objet. Tout cela fonctionne sans accroc.Combinaison de DataTemplates à l'exécution

Les DataTemplates de chaque objet suivent une formule commune, mais contiennent des éléments personnalisés au milieu. Par exemple:

<DataTemplate x:Key="collectibleTemplate"> 
     <Grid> 
      <Grid.RowDefinitions> 
       <RowDefinition Height="Auto" /> 
      </Grid.RowDefinitions> 
      <Border BorderBrush="LightGray" BorderThickness="1"> 
       <Expander IsExpanded="True" Header="{Binding ComponentName}" Background="WhiteSmoke"> 
        <StackPanel> 
         <TextBlock Margin="5,5,5,0" Text="{Binding EditDescription}" TextWrapping="Wrap" /> 

         <!-- This is the only custom part of each template --> 
         <StackPanel Margin="0,10,5,0" Orientation="Horizontal"> 
          <Label Content="Type:" /> 
          <ComboBox Height="22" HorizontalAlignment="Left" SelectedItem="{Binding Path=CollectibleType, Mode=TwoWay}" 
              ItemsSource="{Binding Source={StaticResource collectibleTypeFromEnum}}" /> 
         </StackPanel> 
         <!-- End custom part --> 

         <StackPanel Margin="0,0,0,5"> 
          <Label Content="Available Actions:" > 
           <Label.Style> 
            <Style TargetType="Label"> 
             <Setter Property="Visibility" Value="Visible" /> 
             <Style.Triggers> 
              <DataTrigger Binding="{Binding EditActions.Count}" Value="0"> 
               <Setter Property="Visibility" Value="Collapsed" /> 
              </DataTrigger> 
             </Style.Triggers> 
            </Style> 
           </Label.Style> 
          </Label> 
          <ItemsControl ItemsSource="{Binding EditActions}"> 
           <ItemsControl.ItemTemplate> 
            <DataTemplate> 
             <Button Command="{Binding}" Content="{Binding Title}" ToolTip="{Binding ToolTip}" Margin="5,0,5,0"/> 
            </DataTemplate> 
           </ItemsControl.ItemTemplate> 
          </ItemsControl> 
         </StackPanel> 
        </StackPanel> 
       </Expander> 
      </Border> 
     </Grid> 
    </DataTemplate> 

Comme vous pouvez le voir il y a beaucoup de XAML partagé, en enveloppant une petite section personnalisée au milieu.

Des modèles de données supplémentaires seront écrits par d'autres ingénieurs (ils voudront en créer un pour chaque nouveau type d'objet qu'ils ajouteront), donc je suis intéressé à faire de la création d'un nouveau DataTemplate infaillible et indolore possible. Pas de copie de l'intégralité de DataTemplate avec le "truc" personnalisé ajouté au milieu, bien sûr - mais je ne suis pas non plus parti pour extraire des parties du modèle comme pièces réutilisables et les référencer parce qu'il conduit encore à beaucoup de code dupliqué chaque nouveau DataTemplate, et cela signifie des erreurs possibles et une maintenabilité difficile. À savoir, ce droit ici est une approche plus maintenable mais se sent encore suboptimale:

<DataTemplate x:Key="collectibleTemplate"> 
     <Grid> 
      <Grid.RowDefinitions> 
       <RowDefinition Height="Auto" /> 
      </Grid.RowDefinitions> 
      <Border BorderBrush="LightGray" BorderThickness="1"> 
       <Expander IsExpanded="True" Header="{Binding ComponentName}" Background="WhiteSmoke"> 
        <StackPanel> 
         <TextBlock Margin="5,5,5,0" Text="{Binding EditDescription}" TextWrapping="Wrap" /> 

         <!-- This is the only custom part of each template --> 
         [...] 
         <!-- End custom part --> 

         <ContentPresenter Content="{StaticResource AvailableActions}" /> 

        </StackPanel> 
       </Expander> 
      </Border> 
     </Grid> 
    </DataTemplate> 

    <StackPanel Margin="0,0,0,5" x:Key="AvailableActions" x:Shared="false"> 
     <Label Content="Available Actions:" > 
      <Label.Style> 
     <!-- 
     [Bottom half of shared XAML from the first example, offloaded here] 
     --> 
    </StackPanel> 

Alors: quelle est ma meilleure stratégie pour résoudre ce problème? Je suis toujours bloqué avec l'utilisation de DataTemplates parce que c'est le seul élément qu'un ListBox ItemTemplateSelector accepte. Est-il possible de créer un DataTemplate composé dans le DataTemplateSelector? Je fournirais le DataTemplate stock partagé par tous les objets et les références DataTemplateSelector dans le bit de XAML personnalisé requis pour chaque type d'objet. D'autres ingénieurs se connecteraient à ce comportement de code généralisé. Je ne suis pas sûr, tâtonnant un peu dans l'obscurité ici comme s'il y a un modèle qui me permet de résoudre ceci élégamment.

Et, juste pour référence: mon DataTemplateSelector actuel est super simple. C'est là que je m'attendrais à construire le DataTemplate final, plutôt que de simplement en renvoyer un qui soit codé en dur dans XAML.

public class NodeComponentDataTemplateSelector : DataTemplateSelector 
{ 
    public override DataTemplate SelectTemplate(object item, DependencyObject container) 
    { 
     FrameworkElement element = container as FrameworkElement; 

     if (element != null && item != null) 
     { 
      if (item is CollectibleComponent) 
       return element.FindResource("collectibleTemplate") as DataTemplate; 

      // [...] 
     } 
    } 
} 

Répondre

1

Vous pouvez créer le DataTemplate dynamiquement en utilisant la méthode XamlReader.Parse ou XamlReader.Load, .: par exemple

string template = "<DataTemplate xmlns =\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\" xmlns:x =\"http://schemas.microsoft.com/winfx/2006/xaml\"><StackPanel>[PLACEHOLDER]</StackPanel></DataTemplate>".Replace("[PLACEHOLDER]", "...custom code..."); 
return System.Windows.Markup.XamlReader.Parse(template) as DataTemplate; 

Les parties personnalisées peuvent être définies comme UserControls.

Je crains qu'il n'y ait aucun moyen de baser un DataTemplate sur un autre en pur XAML.

+0

J'ai trouvé cette page assez utile après avoir lu plus sur XamlParser. http://www.ikriv.com/dev/wpf/DataTemplateCreation/ – Drakestar

+1

Je suis en train de mettre en œuvre la solution dès maintenant. Juste appris de l'expérience pour ne pas accepter une réponse jusqu'à ce que je suis réellement fait :) (Mais je le ferai.) – Drakestar

1

Vous pouvez créer une nouvelle CustomControl qui correspond à vos besoins. Il va appliquer le style par lui-même et vous pouvez donner DepdendencyProperties supplémentaires pour le rendre plus pratique. En fin de compte, vous pouvez toujours le mettre dans un DataTemplate pour l'utiliser avec votre DataTemplateSelector.

+0

Bien que cela puisse fonctionner, il est préférable d'ajouter des détails expliquant votre réponse. Vous pouvez trouver plus de conseils ici: [réponse] – ItamarG3