2010-06-23 4 views
4

Je souhaite créer un élément WPF qui, au moment de l'exécution, contrôle entièrement ses éléments enfants - en ajoutant et en supprimant l'interface utilisateur enfant lorsque ses propriétés changent. Quelque chose ressemble à ce que fait ItemsControl lorsque vous modifiez sa propriété ItemsSource, bien que dans mon cas, il n'y aura qu'un seul enfant.Élément WPF qui crée dynamiquement des enfants (encapsulés) à l'exécution

Il s'agira d'un conteneur d'affichage pour MVVM. Lorsque vous lui donnez un modèle ou un ViewModel, il crée par magie la vue correcte et tout le câblage. Il n'est pas nécessaire que mon conteneur de vue puisse être modélisable (puisqu'il crée des vues définies par l'utilisateur, qui sont des UserControls et possèdent leurs propres modèles), et je préférerais qu'il encapsule autant que possible. Je pourrais probablement le faire facilement en descendant de quelque chose comme Grid, et en ajoutant des contrôles enfants quand mes propres propriétés changent; mais Grid expose publiquement sa collection d'éléments enfants et permet à quiconque d'ajouter et de supprimer des éléments.

De quelle classe WPF devrais-je descendre pour une encapsulation maximale et comment y ajouter des éléments enfants lors de l'exécution? En fonction de ma compréhension des documents, j'ai essayé d'utiliser FrameworkElement et AddVisualChild, juste pour voir si je pouvais créer des contrôles enfants à l'exécution. Je ne suis pas clair si le AddLogicalChild est nécessaire, mais je mets en juste au cas où:

public class ViewContainer : FrameworkElement { 
    private TextBlock _child; 

    public ViewContainer() { 
     _child = new TextBlock { Text = "ViewContainer" }; 
     AddLogicalChild(_child); 
     AddVisualChild(_child); 
     InvalidateMeasure(); 
    } 

    public object Content { get; set; } 

    protected override Size ArrangeOverride(Size finalSize) { 
     _child.Arrange(new Rect(finalSize)); 
     return finalSize; 
    } 
    protected override Size MeasureOverride(Size availableSize) { 
     _child.Measure(availableSize); 
     return _child.DesiredSize; 
    } 
} 

Quand je mets un ViewContainer dans une fenêtre et exécuter, je pense voir un TextBlock dire « ViewContainer ". Mais à la place, je vois juste une fenêtre vide. Alors, évidemment, il me manque quelque chose.

Comment puis-je corriger le code ci-dessus pour que le contrôle "enfant" apparaisse au moment de l'exécution, mais ne soit pas exposé pour que les autres puissent jouer avec (pas plus que ce qui peut être évité)?

Répondre

6

Pour répondre à votre question spécifique, vous devez également remplacer les propriétés GetVisualChild et VisualChildrenCount pour permettre à votre élément enfant d'être affiché.

+0

Cool - ça l'a fait. Bien que je sois un peu perplexe quant à la raison pour laquelle il y a un AddVisualChild s'il ne l'ajoute réellement * à * rien. –

+1

Ah, je vois. Il met l'enfant au courant de ses parents; mais beaucoup de parents n'auront pas besoin d'une liste d'enfants (n'en ayant pas ou seulement un), de sorte qu'il laisse le choix - et la responsabilité - de stockage au parent. Séparation agressive des préoccupations: l'une des choses que j'aime particulièrement chez WPF. Mais cela signifie que le nom "AddVisualChild" est trompeur, car il suggère qu'il l'ajoute à une sorte de liste, et ce n'est pas le cas. –

0

Avez-vous pensé à tirer parti de la prise en charge par WPF des DataTemplates implicites?

La façon dont j'ai traité une exigence similaire à la vôtre est en utilisant un ContentControl. Je lie la propriété Content à mon ViewModel. Je vérifie ensuite que dans Data Dictionnaires référencé quelque part dans l'arborescence au-dessus du ContentControl, j'ai des DataTemplates définis pour tous les types de ViewModels qui pourraient être affectés à la propriété de contenu. De cette façon, WPF prend en charge le câblage de la vue correcte pour mon ViewModel.

+0

Oui, je l'ai déjà fait, mais cette fois je veux aller plus loin - je veux que mon ViewContainer puisse prendre un modèle, et découvrir automatiquement et créer le ViewModel correct pour cela, et aller de là à la vue. Je ne pense pas que DataTemplate.DataType m'y amènera sans aide. –

+0

bien sûr, DataTemplate.DataType ne conviendra pas, mais il existe DataTemplateSelector qui pourrait cependant. –

Questions connexes