UserControls et viewmodel en Vue de la Première MVVM
Je suis obligé d'utiliser d'Abord la Vue de MVVM dans une application WPF et j'ai du mal à voir comment il peut être fait de travailler en toute élégance.
La racine du problème se trouve avec imbriqué UserControls
. Dans une architecture MVVM chaque UserControl
a besoin d'avoir son point de vue, le modèle attribués à ses DataContext
, ce qui maintient les expressions de liaison simple, et en plus c'est aussi un moyen de WPF pour instancier une vue générée par DataTemplate
.
Mais si un enfant UserControl
a des propriétés de dépendance dont le parent a besoin de se lier à son propre viewmodel puis, le fait que l'enfant UserControl
a son DataContext
réglé à sa propre viewmodel signifie qu'une "implicite" chemin de liaison dans le parent de fichier XAML permettra de résoudre à l'enfant du viewmodel au lieu de celle du parent.
Pour contourner ce tous les parents de tous les UserControl
dans l'application aura besoin d'utiliser explicitement nommé liaisons pour tout par défaut (qui est verbeux, moche et susceptible de causer des erreurs), ou qu'il va avoir à savoir si un contrôle spécifique a son DataContext
réglé à sa propre viewmodel ou pas et d'utiliser la syntaxe de liaison, (qui est également susceptible de causer des erreurs, et une grave violation de base de l'encapsulation).
Après des jours de recherche je n'ai pas rencontré un seul demi décent solution à ce problème. La chose la plus proche à une solution que j'ai trouver est le réglage de la UserControl's
viewmodel à l'intérieure d'un élément de la UserControl
(le plus haut Grid
ou quoi que ce soit), ce qui laisse tout de même vous face à un problème en essayant de lier les propriétés de la UserControl
lui-même à ses propres viewmodel! (ElementName
de liaison ne fonctionne pas dans ce cas, parce que la liaison puisse être déclaré avant le nom de l'élément avec le viewmodel affecté à sa DataContext
).
Je soupçonne que la raison de ne pas beaucoup d'autres personnes ce qu'ils sont soit à l'aide viewmodel première MVVM qui n'ont pas ce problème, ou qu'ils utilisent d'abord la vue de MVVM en conjonction avec une injection de dépendance de la mise en œuvre que amelliorates cette question.
Quelqu'un aurait-il une solution propre pour cela, s'il vous plaît?
Mise à JOUR:
Exemple de code comme demandé.
<!-- MainWindow.xaml -->
<Window x:Class="UiInteraction.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:UiInteraction"
Title="MainWindow" Height="350" Width="525"
x:Name="_this">
<Window.DataContext>
<local:MainWindowVm/>
</Window.DataContext>
<StackPanel>
<local:UserControl6 Text="{Binding MainWindowVmString1}"/>
</StackPanel>
</Window>
namespace UiInteraction
{
//MainWindow viewmodel.
class MainWindowVm
{
public string MainWindowVmString1
{
get { return "MainWindowVm.String1"; }
}
}
}
<!-- UserControl6.xaml -->
<UserControl x:Class="UiInteraction.UserControl6" x:Name="_this"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:UiInteraction">
<UserControl.DataContext>
<local:UserControl6Vm/>
</UserControl.DataContext>
<StackPanel>
<!-- Is bound to this UserControl's own viewmodel. -->
<TextBlock Text="{Binding UserControlVmString1}"/>
<!-- Has its value set by the UserControl's parent via dependency property. -->
<TextBlock Text="{Binding Text, ElementName=_this}"/>
</StackPanel>
</UserControl>
namespace UiInteraction
{
using System.Windows;
using System.Windows.Controls;
//UserControl code behind declares DependencyProperty for parent to bind to.
public partial class UserControl6 : UserControl
{
public UserControl6()
{
InitializeComponent();
}
public static readonly DependencyProperty TextProperty = DependencyProperty.Register(
"Text", typeof(string), typeof(UserControl6));
public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
}
}
namespace UiInteraction
{
//UserControl's viewmodel.
class UserControl6Vm
{
public string UserControlVmString1
{
get { return "UserControl6Vm.String1"; }
}
}
}
Il en résulte:
Système.De Windows.Erreur de données: 40 : BindingExpression erreur de chemin d'accès:
'MainWindowVmString1 biens ne se trouvent pas sur l '"objet" "UserControl6Vm'
(HashCode=44204140)'. BindingExpression:Path=MainWindowVmString1;
DataItem='UserControl6Vm' (HashCode=44204140); élément cible est
'UserControl6' (Nom='_ce'); cible de la propriété est de "Texte" (type
'String')
parce que dans MainWindow.xaml
la déclaration <local:UserControl6 Text="{Binding MainWindowVmString1}"/>
est de tenter de résoudre MainWindowVmString1
sur UserControl6Vm
.
Dans UserControl6.xaml
en commentant la déclaration de la DataContext
et la première TextBlock
le code fonctionne, mais le UserControl
besoin d'un DataContext
. Dans MainWIndow1
à l'aide d'un ElementName
au lieu d'une implict chemin de liaison, mais pour utiliser la ElementName
syntaxe de liaison vous devez savoir que la UserControl
assigne son viewmodel à son DataContext
(encapsulation échec) ou altenatively adopter une politique sur l'utilisation ElementName
liaisons partout. Ni de ce qui est attrayant.
Je sais que cette question est un peu vieux et juste heurté par la Communauté, mais les cités approche de "
UserControl
's viewmodel à l'intérieur de l'élément" doit être la solution. Je serais intéressé de voir des exemples où une liaison de l'objet UserControl propriétés pour le viewmodel est nécessaire typique UserControl paramètres sont purement de l'INTERFACE utilisateur de choses se rapportant à ou devrait être mis sur l'utilisation plutôt que de la déclaration.Tout UserControl qui accède à des non-trivial de données ou de configuration aurez souvent besoin d'avoir ses contrôles liés à son viewmodel. E. g. un widget réglages, ou l'affichage des données du widget. Seuls les plus trival composants de l'INTERFACE utilisateur ont pas de connexion à un modèle de données sous-jacente à tous, par exemple, un bouton où le texte du bouton est définie par l'intermédiaire de la propriété de dépendance obligatoire dans l'INTERFACE utilisateur.
OriginalL'auteur Neutrino | 2013-01-21
Vous devez vous connecter pour publier un commentaire.
Une solution immédiate consiste à utiliser un
RelativeSource
et mis à la recherche de laDataContext
d'un parentUserControl
:Vous pourrait également traiter l'enfant viewmodel en tant que propriétés de la société mère viewmodel, et de le propager à partir de la mère. De cette façon, le parent viewmodel est conscient de la enfants de sorte qu'il peut mettre à jour leurs propriétés. L'enfant viewmodel peut aussi avoir un
"Parent"
de la propriété qui contient une référence à la société mère, injecté par le parent itelf lors de leur création, qui peut accorder un accès direct à la mère.MODIFIER
Une autre approche plus complexe et plus va faire le parent
DataContext
disponible pour l'enfantUserControl
utilisation d'une propriété attachée. Je n'ai pas entièrement mis en œuvre, mais il s'agit d'une propriété attachée à la demande de la fonction de (quelque chose comme"HasAccessToParentDT"
), dans lequelDependencyPropertyChanged
événement vous que le raccordement de la Charge et de laUnload
événements de laChildUserControl
, l'accès à laParent
propriété (disponible si le contrôle est chargé) et de lier sonDataContext
à une deuxième propriété attachée,"ParentDataContext"
, qui pourrait ensuite être utilisée dans le code xaml.Le problème avec la deuxième approche est que si le parent veut se lier à l'un des ChildControl dépendance des propriétés, en plus de son DataContext alors que nécessite également un alambiqué expression de liaison parce que si l'un des assigné liaisons est le ChildControl du DataContext puis qui affecte l'implicite de source de liaison pour toutes les autres expressions de liaison.
OriginalL'auteur Arthur Nunes
La solution la plus évidente est vraiment à l'aide de la RelativeSource. La liaison elle-même n'a pas l'air très jolie, mais c'est en fait très commun de voir. Je ne voudrais pas de l'éviter - c'est exactement le scénario pourquoi il est là.
Une autre approche que vous pouvez utiliser est une référence à un parent viewmodel, si c'est logique. Comme j'ai un Plan de vol, qui présente une liste de point de navigation et de son interface graphique "carte" de l'autre côté. La liste de ces points est une vue séparée séparée viewmodel:
Le point de vue peut se lier à cette propriété comme ceci:
Cependant composer des viewmodels comme c'est souvent très discutable.
Je suis d'accord que l'utilisation de RelativeSource de la manière proposée dans Arthur Nune réponse est une meilleure approche. D'une part, il n'est pas tout à fait répondre à la question car il ne peut pas être considéré du point de vue syntaxique élégant, mais d'un autre côté, elle est localisée en solution générique. Mais comment est l'utilisation de RelativeSource mieux que x:nom de l'élément d'INTERFACE parent et à l'aide d'un binding ElementName si? E. g. Text="{Binding DataContext.Texte, ElementName=_ce}"
Référencement parent viewmodel est sûrement pas la solution générique - je ici seulement comme un exemple d'une approche différente (bien que vous pouvez également utiliser une injection de dépendance dans certains cas, mais ça ne fonctionne toujours pas à résoudre le problème).
Le RelativeSource peut être mieux car il n'a pas d'associer étroitement l'un des éléments à d'autres qui à mon humble avis est le meilleur, cependant YMMV.
OriginalL'auteur eMko