2009-03-25 12 views
20

Ich habe ein Szenario, in dem ich nicht wirklich weiß, wie Daten an Steuerelemente, die in einem UserControl gehostet werden, an mehrere Datenkontexte gebunden werden.WPF, das mehrere Steuerelemente an verschiedene Datenkontexte bindet

Die Daten i stammt aus zwei Klassen binden möchten

UserInfo, UserExtendedInfo 

Die Datacontext der Usercontrol Userinfo eingestellt ist, so kann ich die meisten Kontrollen leicht tun die folgenden

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

aber ich don binden Ich weiß nicht, wie man Eigenschaften aus der UserExtendedInfo-Klasse einfach bindet. Mein erster Gedanke war, den Datenkontext jedes Steuerelements festzulegen, das die Daten von UserExtendedInfo verwenden möchte, damit ich dasselbe tun konnte. Aber das scheint umständlich, da ich jedes einzelne manuell zuordnen müsste. Die Daten für UserExtendedInfo müssen jedes Mal aus der Datenbank abgerufen werden, wenn das Benutzersteuerelement sichtbar wird, damit es nicht nicht mehr synchron ist.

XAML:

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

-Code Behind:

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 

Antwort

25

ich vielleicht über Einwickeln Ihr Benutzerobjekt in einer separaten Klasse denken würde dann die Datacontext Eigenschaften von Unterplatten Einstellung, die die Daten enthalten.

Zum Beispiel:

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

Dann in Ihrem 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> 

Dies setzt voraus, dass Ihre Userinfo-Klasse eine Eigenschaft von E-Mail hat

und

Das hat Ihre Klasse UserExtendedInfo eine Eigenschaft von Locale und AboutMe

+0

Ok, das macht viel Sinn. So viel neues Zeug in WPF zu lernen :) – Alex

+1

Anstatt ein neues Modell zu erstellen, dessen einziger Zweck es ist, diese bestimmte Ansicht zu unterstützen, sollten Sie einfach UserInfo und UserExtendedInfo als öffentliche Eigenschaften eines ViewModel hinzufügen, die Sie an die Ansicht binden können. (Siehe Rich's [Antwort] (http://stackoverflow.com/questions/679933/wpf-binding-multiple-controls-to-different-datacontexts/680052#680052)). – totorocat

7

Hier ist M-V-VM sehr praktisch. Die Idee (wie ich es zumindest verstehe ... für mich immer noch sehr neu) ist, dass das Fenster selbst an eine "ViewModel" -Klasse gebunden ist. Die ViewModel-Klasse ist nur eine Klasse, die alle Daten so darstellt, dass Ihre gesamte Seite Zugriff auf alles hat, was sie benötigt ... sie fasst einfach alle Objekte zusammen, an die Sie in einer Klasse binden müssen ... und Sie legen den DataContext des Fensters (oder der Seite) auf eine Instanz dieser Klasse fest. Ihre UserInfo-Instanzen und UserInfoExtended-Instanzen sind öffentliche Eigenschaften des ViewModel-Objekts. Sie verwenden einfach den Pfad Ihres Bindungselements, um die entsprechenden Eigenschaften der entsprechenden Objekte zu ermitteln, an die Sie die einzelnen Steuerelemente binden möchten.

Es gibt ein großartiges (aber ziemlich langes) Video, das dieses Muster erklärt, und es geht durch ein vollständiges Beispiel, das viele Möglichkeiten zum Erreichen dieser und vieler anderer Gründe zeigt, warum dies ein praktisches und skalierbares Modell für die Verwendung in einer WPF-App ist. Es umfasst auch viele Funktionen von WPF sowie eine Einführung in die Abhängigkeitsinjektion, die am Beispiel auch sehr relevante Themen sind.

Hier ist ein Link zu dem Blog-Post, die einen Link zu dem Video enthält Ich spreche von:

EDIT: Blog Post entfernt wurde (diese Antwort ist ziemlich alt).Hier ist das Video auf YouTube:

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

+0

nur eine Anmerkung, das ViewModel muss keine Abhängigkeitseigenschaften haben. Es muss nur INotifyPropertyChanged implementiert werden, wenn die Steuerelemente Änderungen in einer ihrer Eigenschaften widerspiegeln sollen. –

+0

Link ist tot, würden Sie bitte den Link aktualisieren? –

+0

@HassanTareq Gefunden auf YouTube und aktualisiert den Link – Rich

6

reich und bendewey gute Antworten hatten. Wenn ich heute dasselbe Thema in Silverlight anstelle von WPF erkunde, stellte ich fest, dass es nicht notwendig ist, mehrere DataContexte einzurichten. Revidieren bendewey Vorbild:

<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> 

die Bindung Pfad erhalten Sie die Flexibilität Bindungen zu mischen und anzupassen, um Eigenschaften verschiedener Klassen, ohne Sorge um die Datacontext der Container Kontrollen.

Sie können auch die Funktionen der UserDataContext-Klasse von bendewey erweitern, indem Sie Eigenschaften hinzufügen, die die Eigenschaften der Klassen UserInfo und UserExtendedInfo ändern. Sie können beispielsweise Vor- und Nachname kombinieren.

Möglicherweise möchten Sie INotifyPropertyChanged implementieren, damit Ihre Steuerelemente beim Zurücksetzen von UserInfo und UserExtendedInfo aktualisiert werden.

Es kann architektonisch besser sein, die zugrunde liegenden Klassen UserInfo und UserExtendedInfo vollständig vom XAML zu isolieren, indem die erforderlichen Eigenschaften direkt in UserDataContext verfügbar gemacht werden, wodurch Binding Path nicht mehr erforderlich ist.

+1

Dies ist eine Verbesserung der Antwort von bendueway (+1), aber ich würde ein paar mehr Dinge verbessern: Sie brauchen nicht "Path =", und in 95% der Fälle ich würde statt einer expliziten Klasse anonyme C# -Typen verwenden. –

15

Hier ist die einfachste Methode von allen, und es funktioniert sehr gut.

Im Code-behind in dem Sie den Kontext gesetzt, einfach einen anonymen Typ verwenden, um alle gewünschten Werte enthalten:

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

Alternativ:

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

Im XAML Sie etwas binden können Sie können in zwei Ebenen binden, wie andere Antworten beschrieben haben:

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

Danke! Hat mir eine Menge geholfen! –

Verwandte Themen