2009-03-25 7 views
20

J'ai un scénario dans lequel je ne sais pas vraiment comment lier des données à des contrôles hébergés dans un UserControl à plusieurs datacontexts.WPF lier plusieurs contrôles à différentes datacontexts

Les données que je veux lier provient de 2 classes

UserInfo, UserExtendedInfo 

Le datacontext du UserControl est réglé sur UserInfo donc je peux lier la plupart des contrôles faisant facilement les éléments suivants

<Label Name="LblEmail" Text="{Binding Email}" /> 

Cependant, je don Je ne sais pas comment lier facilement les propriétés de la classe UserExtendedInfo. Ma première pensée était de définir le contexte de chaque contrôle qui veut utiliser les données de UserExtendedInfo pour que je puisse faire la même chose. Mais cela semble lourd car je devrais attribuer manuellement chacun individuellement. Les données de UserExtendedInfo doivent être extraites de la base de données chaque fois que UserControl devient visible afin de ne pas être désynchronisé.

XAML:

<Label Name="LblTest" Text="{Binding Locale}" /> 

code Derrière:

Private Sub UserManager_IsVisibleChanged(ByVal sender As System.Object, ByVal e As System.Windows.DependencyPropertyChangedEventArgs) 
     If DirectCast(e.NewValue, Boolean) Then 
      Dim user As UserInfo = DirectCast(DataContext, UserInfo) 

      If user IsNot Nothing Then 
       Dim usrExt As UserExtenedInfo = UserController.GetUserExtended(user.userID) 

       LblTest.DataContext = usrExt 
      Else 
       Throw New ArgumentException("UserId doesn't exist or is less than 1") 
      End If 
     End If 
    End Sub 

Répondre

25

Je pense peut-être à propos de l'emballage de votre objet utilisateur dans une classe séparée puis en définissant les propriétés DataContext des sous-groupes qui contiennent les données.

Par exemple:

public class UserDataContext 
{ 
    public UserInfo UserInfo { get; set; } 
    public UserExtendedInfo UserExtendedInfo { get; set; } 
} 

Ensuite, dans votre UserControl.xaml:

<!-- Binding for the UserControl should be set in its parent, but for clarity --> 
<UserControl DataContext="{Binding UserDataContext}"> 
    <StackPanel> 
    <Grid DataContext="{Binding UserInfo}"> 
     <TextBlock Text="{Binding Email}" /> 
    </Grid> 
    <Grid DataContext="{Binding UserExtendedInfo}"> 
     <TextBlock Text="{Binding Locale}" /> 
     <TextBlock Text="{Binding AboutMe}" /> 
    </Grid> 
    </StackPanel> 
</UserControl> 

Cela suppose que votre classe UserInfo possède une propriété de Email

et

Que votre classe UserExtendedInfo a une propriété de Locale et AboutMe

+0

Ok cela a beaucoup de sens. Beaucoup de choses nouvelles à apprendre dans WPF :) – Alex

+1

Au lieu de créer un nouveau modèle dont le seul but est de supporter cette vue particulière, il suffit d'ajouter UserInfo et UserExtendedInfo en tant que propriétés publiques d'un ViewModel que vous pouvez lier à la vue. (Voir Rich [réponse] (http://stackoverflow.com/questions/679933/wpf-binding-multiple-controls-to-different-datacontexts/680052#680052)). – totorocat

7

C'est ici que M-V-VM est très utile. L'idée (si je la comprends au moins ... encore très nouvelle pour moi) est que la fenêtre elle-même est liée à une classe "ViewModel". La classe ViewModel est juste une classe qui représente toutes les données de manière à ce que toute votre page ait accès à tout ce dont elle a besoin ... elle rassemble simplement tous les objets différents auxquels vous devrez vous lier dans une classe ... et vous définissez le DataContext de la fenêtre (ou de la page) sur une instance de cette classe. Vos instances UserInfo et UserInfoExtended sont des propriétés publiques de l'objet ViewModel, et vous utilisez simplement le chemin de votre élément de liaison pour vous guider dans les propriétés appropriées des objets appropriés auxquels vous souhaitez lier chaque contrôle. Il y a une excellente vidéo (mais assez longue) expliquant ce modèle, et elle présente un exemple complet illustrant de nombreuses façons d'y parvenir et de nombreuses raisons différentes pour lesquelles ce modèle est pratique et évolutif à utiliser dans une application WPF. Il couvre également de nombreuses fonctionnalités de WPF ainsi qu'une introduction à l'injection de dépendance, qui sont aussi des sujets très pertinents, compte tenu de l'exemple.

Voici un lien vers le blog qui contient un lien vers la vidéo dont je parle:

EDIT: Blog post a été supprimé (cette réponse est assez vieux).Voici la vidéo sur YouTube:

https://www.youtube.com/watch?v=BRxnZahCPFQ

+0

juste une note, le ViewModel n'a pas besoin d'avoir des propriétés de dépendance. Il suffit d'implémenter INotifyPropertyChanged si vous souhaitez que les contrôles reflètent la modification dans l'une de ses propriétés. –

+0

Lien est mort, pourriez-vous s'il vous plaît mettre à jour le lien? –

+0

@HassanTareq Trouvé sur YouTube et mis à jour le lien – Rich

6

Les riches et bendewey avaient de bonnes réponses. Explorant ce même sujet aujourd'hui dans Silverlight au lieu de WPF, j'ai trouvé qu'il n'est pas nécessaire d'établir plusieurs DataContexts. L'exemple de la révision bendewey:

<UserControl DataContext="{Binding UserDataContext}"> 
    <StackPanel> 
     <TextBlock Text="{Binding Path=UserInfo.Email}" /> 
     <TextBlock Text="{Binding Path=UserExtendedInfo.Locale}" /> 
     <TextBlock Text="{Binding Path=UserExtendedInfo.AboutMe}" /> 
    </StackPanel> 
</UserControl> 

Utilisation du chemin de reliure vous gagnez la possibilité de mélanger et les liaisons de correspondance aux propriétés des différentes classes sans se soucier du DataContext des conteneurs des contrôles.

Vous pouvez également étendre les capacités de la classe UserDataContext de bendewey en ajoutant des propriétés qui manipulent les propriétés des classes UserInfo et UserExtendedInfo. Vous pourriez, par exemple, combiner le prénom et le nom de famille.

Vous pouvez implémenter INotifyPropertyChanged afin que vos contrôles soient mis à jour lorsque vous réinitialisez UserInfo et UserExtendedInfo.

Il peut être préférable sur le plan architectural d'isoler entièrement les classes UserInfo et UserExtendedInfo sous-jacentes du code XAML en exposant directement les propriétés requises dans UserDataContext, éliminant ainsi le besoin de Binding Path.

+1

Ceci est une amélioration sur la réponse de Bendeway (+1), mais je voudrais améliorer un peu plus de choses: Vous n'avez pas besoin de "Path =", et dans 95% des cas je utiliserait des types anonymes C# au lieu d'une classe explicite. –

15

Voici la méthode la plus simple de toutes, et cela fonctionne très bien.

Dans le code-behind où vous définissez le contexte, il suffit d'utiliser un type anonyme contenant toutes les valeurs souhaitées:

DataContext = new 
{ 
    info = FetchUserInfoFromDatabase(), 
    extendedInfo = FetchExtendedUserInfoFromDatabase(), 
}; 

Dans le XAML vous pouvez lier à quoi que ce soit:

<UserControl> 
    <StackPanel> 
    <TextBlock Text="{Binding info.Email}" /> 
    <TextBlock Text="{Binding extendedInfo.Locale} /> 
    ... 

Alternativement vous pouvez lier en deux niveaux comme d'autres réponses l'ont décrit:

<UserControl> 
    <StackPanel> 
    <Grid DataContext="{Binding info}"> 
     <TextBlock Text={Binding Email}"> 
     ... 
+0

Merci! A m'a aidé une tonne! –

Questions connexes