2009-03-25 12 views
20

Ho uno scenario in cui non so davvero come associare i dati ai controlli ospitati in un controllo utente a più datacontex.WPF che associa più controlli a diversi datacontatti

i dati che voglio legare viene da 2 classi

UserInfo, UserExtendedInfo 

DataContext di UserControl è impostata su UserInfo modo che io possa legare la maggior parte dei comandi facilmente facendo quanto segue

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

Comunque io don sapere come associare facilmente le proprietà dalla classe UserExtendedInfo. Il mio pensiero iniziale era di impostare il datacontext di ciascun controllo che desidera utilizzare i dati da UserExtendedInfo in modo da poter fare lo stesso. Ma questo sembra ingombrante come dovrei assegnare manualmente ciascuno individualmente. I dati per UserExtendedInfo devono essere recuperati dal database ogni volta che UserControl diventa visibile in modo che non vada fuori sincrono.

XAML:

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

Codice Dietro:

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 

risposta

25

avrei forse pensare di confezionamento proprio oggetto utente in una classe separata, allora l'impostazione delle proprietà DataContext di sotto-pannelli che contengono i dati.

Ad esempio:

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

Poi, nel tuo 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> 

Questo presuppone che la classe UserInfo ha una proprietà di e-mail

e

Che la tua classe UserExtendedInfo abbia una proprietà di Locale e AboutMe

+0

Ok, questo ha molto senso. Tante cose nuove da imparare in WPF :) – Alex

+1

Invece di creare un nuovo modello il cui unico scopo è supportare questa particolare vista, è sufficiente aggiungere UserInfo e UserExtendedInfo come proprietà pubbliche di un ViewModel che è possibile associare alla vista. (Vedi Rich's [answer] (http://stackoverflow.com/questions/679933/wpf-binding-multiple-controls-to-different-datacontexts/680052#680052)). – totorocat

7

Qui è dove M-V-VM è molto utile. L'idea (come la capisco almeno ... ancora molto nuova per me) è che la finestra stessa è legata a una classe "ViewModel". La classe ViewModel è solo una classe che rappresenta tutti i dati in modo che l'intera pagina abbia accesso a tutto ciò di cui ha bisogno ... riunisce semplicemente tutti i diversi oggetti di cui avrai bisogno per legare in una classe ... e imposta il DataContext della Finestra (o Pagina) su un'istanza di questa classe. Le istanze UserInfo e UserInfoExtended sono proprietà pubbliche dell'oggetto ViewModel e si utilizza semplicemente il percorso dell'elemento di binding per ottenere le proprietà appropriate degli oggetti appropriati a cui si desidera associare ciascun controllo.

C'è un grande (ma piuttosto lungo) video che spiega questo modello, e passa attraverso un esempio completo che illustra molti modi per realizzare questo e molti diversi motivi per cui questo è un modello conveniente e scalabile da utilizzare in un'app WPF. Copre anche molte caratteristiche del WPF e un'introduzione all'iniezione di dipendenza, che sono anche argomenti molto rilevanti, dato l'esempio.

Ecco un link al post del blog che contiene un link al video che sto parlando di:

EDIT: blog post è stato rimosso (questa risposta è abbastanza vecchio).Ecco il video su YouTube:

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

+0

solo una nota, il ViewModel non ha bisogno di avere proprietà di dipendenza. È sufficiente implementare INotifyPropertyChanged se si desidera che i controlli riflettano il cambiamento in una delle sue proprietà. –

+0

Link è morto, per favore aggiorna il link? –

+0

@HassanTareq Trovato su YouTube e aggiornato il collegamento – Rich

6

ricchi e bendewey avuto buone risposte. Esplorando questo stesso argomento oggi in Silverlight invece che in WPF, ho scoperto che non è necessario stabilire più DataContexts. Rivedere l'esempio di 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> 

Utilizzando il percorso di associazione si guadagna la possibilità di mescolare e attacchi di corrispondenza per le proprietà di classi diverse, senza preoccupazione per il DataContext dei contenitori dei controlli.

È anche possibile estendere le funzionalità della classe UserDataContext di bendewey aggiungendo proprietà che manipolano le proprietà delle classi UserInfo e UserExtendedInfo. Ad esempio, potresti combinare il nome e il cognome.

È possibile implementare INotifyPropertyChanged in modo che i controlli vengano aggiornati quando si reimposta UserInfo e UserExtendedInfo.

Può essere architetturalmente preferibile isolare interamente le classi UserInfo e UserExtendedInfo sottostanti da XAML esponendo le proprietà richieste direttamente in UserDataContext, eliminando così la necessità di Binding Path.

+1

Questo è un miglioramento della risposta di Bendeway (+1), ma vorrei migliorare alcune altre cose: non è necessario "Path =", e nel 95% dei casi I userei i tipi anonimi C# invece di una classe esplicita. –

15

Ecco il metodo più semplice di tutti e funziona molto bene.

Nel code-behind in cui si imposta il contesto, è sufficiente utilizzare un tipo anonimo contenente tutti i valori desiderati:

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

In XAML è possibile associare a qualsiasi cosa:

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

alternativa è possibile associare in due livelli come hanno risposto altre risposte:

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

Grazie! Mi ha aiutato un sacco! –

Problemi correlati