2016-07-27 28 views
2

Ich frage mich, ob es möglich ist, einen Datenkontext zwischen einem Windows und UserControl in C#/WPF freigegeben.Share DataContext zwischen MainWindows und UserControl

Ich habe ein Hauptfenster wie folgt aus (nicht abgeschlossen):

MainWindow.xaml:

<Window x:Class="MyProject.MainWindow" 
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
     xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
     xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
     xmlns:local="clr-namespace:MyProject" 
     xmlns:v="clr-namespace:MyProject.Views" 
     mc:Ignorable="d" 
     Title="MyProject" > 
    <Window.DataContext> 
     <local:MainViewModel/>   
    </Window.DataContext> 
    <Grid> 
     <v:GenerateView/> 
     <v:ReadView/> 
    </Grid> 
</Window> 

MainViewModel.cs:

public class MainViewModel : ViewModelBase 
{ 
    #region Properties 
    #endregion 

    #region Fields 
    #endregion 

    #region Constructor 
    public MainViewModel() 
     : base() 
    { 
    } 
    #endregion 

    #region Methods 
    #endregion 

    #region Commands 
    #endregion 
} 

In Abhängigkeit von einem zukünftigen Parameter, ich will zeige meine Ansicht GenerateView oder ReadView an. Eigentlich entwickle ich das UserControl GenerateView, aber ich frage mich, ob ich denselben Datacontext verwenden kann.

Nach that post, ich damit begonnen:

<UserControl x:Class="MyProject.Views.GenerateView" 
     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:d="http://schemas.microsoft.com/expression/blend/2008" 
     xmlns:local="clr-namespace:MyProject.Views" 
     xmlns:p="clr-namespace:MyProject.Properties" 
     xmlns:MyProject="clr-namespace:MyProject" 
     mc:Ignorable="d" 
     d:DesignHeight="300" d:DesignWidth="300" 
     DataContext="{Binding DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type MyProject:MainWindow}}}"> 
    <Grid> 
    </Grid> 
</UserControl> 

aber es funktioniert nicht, wenn ich versuche, in Datacontext GenerateView zugreifen zu können, ist null.

Edit:

vergaß ich einen Teil meines Code:

public partial class GenerateView : UserControl 
{ 
    private MainViewModel Context 
    { 
     get 
     { 
      return DataContext as MainViewModel; 
     } 
    } 

    public GenerateView() 
    { 
     InitializeComponent(); 
     Context.PropertyChanged += Context_PropertyChanged; 
    } 

    private void Context_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) 
    { 
     //Action to perform 
    } 
} 

Die Linie Context.PropertyChanged += Context_PropertyChanged; eine Ausnahme werfen, weil Datacontext null ist.

+5

Datacontext geerbt wird auf alle unten Untergeordnete Steuerelemente, falls nicht explizit festgelegt. Versuchen Sie, es aus Ihren Benutzersteuerelementen zu entfernen und legen Sie es nur im Fenster fest. –

+0

Der DataContext wird auch erst nach dem Aufruf des Konstruktors gesetzt. Verwenden Sie das DataContextChanged-Ereignis, um zu wissen, wann es gesetzt ist. – Andrew

+0

@Nudity Ich habe es aus UserControls entfernt und getestet. DataContext ist null im Konstruktor GenerateView, aber nach 'InitializeComponent' aus' MainViewModel' ist DataContext für beide korrekt. Aber ich kann 'Context.PropertyChanged' nicht auf meinem UserControl setzen @Andrew Perfect! –

Antwort

3

Was ist die Hauptanforderung für die Wiederverwendung eines Ansichtsmodells für Windows und seine Unterbenutzersteuerung? es macht keinen Sinn. Haben beide alles gemeinsam?

Meiner Meinung nach erstellen Sie eine MainWindowViewModel und erstellen Sie SubViewModel für Benutzersteuerung. Erstellen Sie die Instanzen von Unteransichtsmodellen in Ihrem MainWindowViewModel, und greifen Sie mithilfe von DataContext.SubViewModel darauf zu.

Indem Sie dies tun, können Sie den Code und die Anwendung gut erhalten, sowie Kodierungsstandards beibehalten und ein komplikationsfreies Viewmodel haben. Wenn Sie alles nur für die Wiederverwendbarkeit verwechseln, verletzen Sie möglicherweise das MVVM-Muster. Lassen Sie verschiedene Views/Fenster ein eigenes Viewmodel haben, da sie nicht gleich sind.

Wenn beide gleich sind, können Sie mit Dependency Properties wiederverwendbare Steuerelemente erstellen.

+0

Mein MainWindows ist ein leeres Fenster. Meine Anwendung dient zum Erzeugen oder Lesen von Bildern. Ich habe ein UserControl mit IHM zum Lesen ('ReadView') und das andere zum Generieren (' GenerateView'). Beide Benutzersteuerelemente verwenden die gleichen fileds/properties und einige allgemeine Methoden. Ich habe gerade angefangen, meine Anwendung zu entwerfen, ich kann sie ändern. Deshalb habe ich meine Frage gestellt, ich möchte MVVM-Muster respektieren und Ihre Antwort scheint sehr interessant. Gibt es eine Möglichkeit, etwas wie 'MainWindowViewModel' mit gemeinsamen Feldern/Methoden und' GenerateViewModel' und 'ReadViewModel' von main zu erstellen? –

+0

@ A.Pissicat: Ja. Es ist offensichtlich möglich. Und das habe ich gesagt. Sie können ein übergeordnetes Ansichtsmodell haben. Sie können jedoch keine Ansichtsmodelle erben. Vielleicht, wenn Sie beide Ansichten in allgemeinen Abhängigkeitseigenschaften verwenden, um wiederverwendbare Ansichten zu haben. Damit Sie diese Eigenschaften aktualisieren können, wenn Sie die Daten ändern möchten. Anstatt 2 Ansichten zu verwenden, können Sie 1 Ansicht verwenden, wenn alle Eigenschaften identisch sind, und die Daten entsprechend ändern. – ViVi

0

Laut Andrew Kommentar I'have die Lösung für mein Problem:

public partial class GenerateView: Usercontrol { privaten MainViewModel Kontext { erhalten { return Datacontext als MainViewModel; } }

public GenerateView() 
{ 
    InitializeComponent(); 
    DataContextChanged += GenerateView_DataContextChanged; 
} 

private void GenerateView_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e) 
{ 
    if (Context != null) 
     Context.PropertyChanged += Context_PropertyChanged; 
} 
private void Context_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) 
{ 
    //Action to perform 
} 

}

Und ich entfernt DataContext="{Binding DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type MyProject:MainWindow}}}" von meinem Usercontrol.

1

ich in der Regel die DataContext gesetzt in meinem Window:

public class MainWindow : Window 
{ 
    InitializeComponent(); 
    ViewModel vm = new ViewModel(); 
    this.DataContext = vm; 
} 

oder manchmal weiter fortgeschritten:

Ich füge eine statische Eigenschaft zu meinem Viewmodel:

public static ViewModel Instance {get; set;} 

public class MainWindow : Window 
{ 
    InitializeComponent(); 
    if(ViewModel.Instance == null) 
    { 
     ViewModel.Instance = new ViewModel();   
    } 
    this.DataContext = ViewModel.Instance; 
} 
+0

Ich frage mich, ob es nicht einfacher ist, das ViewModel als Singleton zu erstellen? –

+0

@ A.Pissicat, das hängt von Ihrem Anwendungsfall ab. Ich bevorzuge es, immer dieselbe Instanz zu verwenden und nicht jedes Mal eine neue VM zu erstellen. Wenn es zu vielen Bindungen kommt, möchte ich nicht alle mit Standardwerten erhalten - stattdessen bekomme ich die Werte, die die VM zuletzt hatte. –