2010-07-03 6 views
4

En utilisant le modèle MVVM, j'ai une paire de classes de modèle de vue qui représentent une hiérarchie de données à deux niveaux, chacune avec un UserControl correspondant qui représente sa vue . Les deux classes de modèle de vue implémentent INotifyPropertyChanged et le modèle de vue de niveau racine expose une propriété qui est pertinente à la fois pour sa propre vue et pour la vue enfant.WPF + MVVM - Comment lier une propriété au contexte de données de la vue parent

La vue de niveau racine acquiert le modèle de vue de niveau racine en tant que contexte de données et affecte explicitement un contexte de données à sa vue. Toutefois, il doit également lier l'une des propriétés de la vue enfant à la propriété partagée mentionnée ci-dessus. Voici comment j'ai essayé d'y parvenir, mais il ne fonctionne pas:

<UserControl x:Name="rootView"> 
    <StackPanel> 

     <!-- other controls here --> 

     <my:ChildView 
      DataContext="{Binding Path=SelectedChild}" 
      EditingMode="{Binding ElementName=rootView, Path=DataContext.EditingMode /> 
    </StackPanel> 
</UserControl> 

Bien qu'il n'y ait pas d'erreurs de liaison d'exécution et la vue de l'enfant se fixe correctement à l'instance de modèle de vue enfant approprié, sa propriété EditingMode est jamais définie. J'ai exécuté des tests pour vérifier que la propriété de modèle de vue correspondante est en cours de modification et qu'il notifie cette modification via INotifyPropertyChanged, mais la liaison ne parvient pas à le détecter.

Existe-t-il un meilleur moyen de déclarer cette liaison ou ai-je fait une erreur architecturale plus basique?

Un grand merci pour vos conseils,

Tim

Mise à jour: Comme l'a demandé, je signale un code pour afficher une version très simplifiée de mes vues et modèles de vue, ainsi que les résultats d'une expérience que j'ai menée qui peut fournir des indices supplémentaires.

// The relevant parts of the ParentViewModel class 
public class ParentViewModel : INotifyPropertyChanged 
{ 
    // Although not shown, the following properties 
    // correctly participate in INotifyPropertyChanged 

    public ChildViewModel SelectedChild { get; private set; } 

    public ContentEditingMode EditingMode { get; private set; } 
} 

// The relevant parts of the ChildViewModel class 
public class ChildViewModel : INotifyPropertyChanged 
{ 
    // No properties of ChildViewModel affect this issue. 
} 

// The relevant parts of the ParentView class 
public partial class ParentView : UserControl 
{ 
    // No properties of ParentView affect this issue. 
} 

// The relevant members of the ChildView class 
public partial class ChildView : UserControl 
{ 
    public static readonly DependencyProperty EditingModeProperty = 
     DependencyProperty.Register(
      "EditingMode", 
      typeof(ContentEditingMode), 
      typeof(PostView) 
     ); 

    public ContentEditingMode EditingMode 
    { 
     get { return (ContentEditingMode)GetValue(EditingModeProperty); } 
     set { SetValue(EditingModeProperty, value); } 
    } 
} 

// The enumeration used for the EditingMode property 
public enum ContentEditingMode 
{ 
    Html, 
    WYSYWIG 
} 

Mon intention est que le DataContext de l'instance de vue parent est assigné une instance de ParentViewModel et il sera, à son tour, affecter la valeur de sa propriété SelectedChild au DataContext du ChildView imbriquée. Tout cela semble fonctionner correctement, mais le problème se pose parce que la liaison entre ParentViewModel.EditingMode et ChildView.EditingMode ne fonctionne pas.

Pour tenter de vérifier s'il y a un problème avec mon expression de liaison, j'introduit un TextBlock adjacent au ChildView et enveloppèrent de façon similaire à la propriété ParentViewModel.EditingMode:

<UserControl x:Name="rootView"> 
    <StackPanel> 

     <!-- other controls here --> 

     <TextBlock Text="{Binding ElementName=rootView, Path=DataContext.EditingMode}" /> 

     <my:ChildView 
      DataContext="{Binding Path=SelectedChild}" 
      EditingMode="{Binding ElementName=rootView, Path=DataContext.EditingMode /> 
    </StackPanel> 
</UserControl> 

Dans ce test, le TextBlock est correctement mis à jour chaque fois que la propriété source change. Cependant, si je mets un point d'arrêt sur le setter de ChildView.EditingMode, il ne sera jamais touché.

Je suis déconcerté!

+0

La définition d'un point d'arrêt sur le setter d'une propriété de dépendance ne fonctionnera pas. Le point entier des propriétés de dépendance est qu'elles sont définies par le système de propriété de dépendance. C'est pourquoi vous ne pouvez pas vous lier aux propriétés CLR ordinaires; les liaisons définissent les propriétés de la cible en appelant 'SetValue'. Si vous souhaitez tracer le paramètre des propriétés de dépendance, vous devez implémenter une fonction de rappel et utiliser la surcharge de 'Register' qui vous permet de le spécifier. –

+0

Merci pour cette précision - cela me sauvera probablement d'innombrables heures de frustration. –

+0

Cela m'aurait sauvé d'innombrables heures de frustration si je l'avais su, je peux vous dire ça. –

Répondre

2

La façon la plus simple de résoudre ce problème est dans votre modèle de vue. Implémentez une propriété EditingMode dans le modèle de vue enfant et liez-la. De cette façon, vous n'avez aucune idée de ce que pourrait être la bonne façon d'établir la liaison. aussi, c'est quelque chose que vous pouvez tester en dehors de l'interface utilisateur.

Modifier

En fait, la solution est à droite pas tout à fait aussi simple, mais il est bon de savoir comment faire.

Ce que vous souhaitez, c'est que EditingMode dans le contrôle enfant hérite efficacement de sa valeur du contrôle parent. Cela ressemble-t-il à quelque chose que n'importe quoi d'autre dans WPF fait? Comme à peu près tous les éléments de cadre qui implémente les propriétés de dépendance?

Implémentez EditingMode comme propriété de dépendance dans le parent et l'enfant UserControl s et utilisez l'héritage de valeur de propriété, comme décrit here. Cela prend entièrement le comportement d'héritage hors du modèle de vue et le place où il appartient.

+0

Merci pour votre suggestion Robert. En fait, Berryl a fait une suggestion similaire presque en même temps! S'il vous plaît voir mon commentaire de réponse. Mes inquiétudes concernant la performance sont-elles justifiées? –

+0

Merci Robert - votre édition est exactement le genre de pensée latérale dont j'ai besoin! Je vais le mettre en œuvre et poster à nouveau si je heurte des obstacles mais, en attendant, j'accepte votre réponse. Merci encore. –

2

Voyez si vous pouvez simplement utiliser un chemin complet pour obtenir le mode d'édition de l'enfant sélectionné:

<my:childView 
     DataContext="{Binding SelectedChild}" 
     EditingMode="{Binding SelectedChild.EditingMode /> 
+0

Merci Berryl, mais cela ne fonctionne pas, car le modèle de vue enfant n'a pas la propriété EditingMode. C'est la raison de la publication - comment lier la vue enfant à une propriété sur le modèle de vue parent. –

+0

Donc, SelectedChild est une propriété de ParentVm et EditingMode est également? C'est trop facile, car alors vous vous lieriez directement à EditingMode. D'où obtenez-vous EditingMode à partir de RootLevelParentVm? Vous devrez peut-être publier le code derrière votre UserControl. – Berryl

+0

Je ne peux pas me lier directement à la propriété EditingMode de la vue enfant car le DataContext affecté à la vue enfant est une instance du modèle de vue enfant, qui n'expose pas cette propriété. Comme vous le suggérez, je posterai une version simplifiée de mon code. –