2017-06-05 4 views
3

Dies ist mein erster Versuch, das MVVM-Modell zu implementieren, also im Voraus zu entschuldigen. Ich habe ein Hauptfenster mit einem eigenen ViewModel. Das Hauptfenster hat dann drei Benutzersteuerungen, die je nach Auswahl im Navigationsmenü geladen werden. Der Datenkontext ändert sich jedoch nicht in das richtige Ansichtsmodell.So fügen Sie den Benutzersteuerelementen im Hauptfenster mehrere ViewModels hinzu

MainWindow.xaml

<Window.DataContext> 
    <VM:MainVM></VM:MainVM> 
</Window.DataContext> 

<Window.Resources> 
    <DataTemplate x:Key="View1Template" DataType="{x:Type VM:CustomerVM}"> 
     <View:Customers DataContext="{Binding VM:CustomerVM}" /> 
    </DataTemplate> 

    <DataTemplate x:Key="View2Template" DataType="{x:Type VM:SuppliersVM}"> 
     <View:Suppliers DataContext="{Binding VM:Suppliers}"/> 
    </DataTemplate> 
</Window.Resources> 

<ContentControl Margin="0,135,0,10" Grid.Column="1"> 
    <ContentControl.Style> 
     <Style TargetType="{x:Type ContentControl}"> 
      <Setter Property="ContentTemplate" Value="{StaticResource View1Template}" /> 
      <Style.Triggers> 
       <DataTrigger Binding="{Binding SwitchView}" Value="0"> 
        <Setter Property="ContentTemplate" Value="{StaticResource View1Template}" /> 
       </DataTrigger> 
       <DataTrigger Binding="{Binding SwitchView}" Value="1"> 
        <Setter Property="ContentTemplate" Value="{StaticResource View2Template}" /> 
       </DataTrigger> 
      </Style.Triggers> 
     </Style> 
    </ContentControl.Style> 
</ContentControl> 

dies sein eigenes Ansichtsmodell hat und Navigation lädt die Ansicht richtig, aber ich einen Befehl habe, die auf Last läuft und seine nicht durchgeführt wird. Dies füllt nur eine Listenansicht. Ich weiß, dass es funktioniert, weil, wenn ich den datacontext des Fensters entferne und es als einstelle, das listview bevölkert wird, aber Navigation funktioniert nicht mehr, weil MainVM entfernt worden ist.

Customer.xaml

<i:Interaction.Triggers> 
    <i:EventTrigger EventName="Loaded"> 
     <i:InvokeCommandAction Command="{Binding LoadCustomersCommand}" /> 
    </i:EventTrigger> 
</i:Interaction.Triggers> 
<Grid> 
    <TextBox Tag="Search Customers" x:Name="searchTextBox" Text="{Binding SearchText, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" HorizontalAlignment="Left" Height="40" Margin="10,9,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="340" Padding="5,6,0,0" FontSize="16"/> 
    <Label Content="{Binding Path=SearchText}" Margin="430,0,436,263" /> 
    <ListView ItemsSource="{Binding Customers}" x:Name="customerListBox" Margin="10,57,10,10" AlternationCount="2" > 
     <ListView.View> 
      <GridView> 
       <GridViewColumn Header="ID" Width="Auto" DisplayMemberBinding="{Binding id}" /> 
       <GridViewColumn Header="NAME" Width="Auto" DisplayMemberBinding="{Binding name}" /> 
       <GridViewColumn Header="ADDRESS" Width="Auto" DisplayMemberBinding="{Binding address1}" /> 
       <GridViewColumn Header="ADDRESS 2" Width="150" DisplayMemberBinding="{Binding address2}" /> 
       <GridViewColumn Header="TOWN" Width="150" DisplayMemberBinding="{Binding town}" /> 
       <GridViewColumn Header="COUNTY" Width="150" DisplayMemberBinding="{Binding county}" /> 
       <GridViewColumn Header="POSTCODE" Width="150" DisplayMemberBinding="{Binding postcode}" /> 
       <GridViewColumn Header="PHONE" Width="150" DisplayMemberBinding="{Binding phone}" /> 
       <GridViewColumn Header="EMAIL" Width="150" DisplayMemberBinding="{Binding email}" /> 
      </GridView> 
     </ListView.View> 
    </ListView> 
</Grid> 

CustomerVM.cs

private string _searchText; 
private readonly ObservableCollection<Customer> _customers = new ObservableCollection<Customer>(); 

public string Title = "Customers"; 

public string SearchText 
{ 
    get { return _searchText; } 
    set 
    { 
     _searchText = value; 
     RaisePropertyChangedEvent("SearchText"); 
    } 
} 

public IEnumerable<Customer> Customers 
{ 
    get { return _customers; } 
} 

public ICommand LoadCustomersCommand 
{ 
    get { return new DelegateCommand(LoadCustomers); } 
} 

public void LoadCustomers() 
{ 
    Customer cus = new Customer { id = 1, name = "sam" }; 
    _customers.Add(cus); 

} 

MainVM.cs

public ICommand NavigationClick 
{ 
    get { return new DelegateCommand(Navigate); } 
} 

public void Navigate() 
{   
    SwitchView = 1; 
} 
+0

Bitte zeigen Sie, wie diese DataTemplates im XAML verwendet werden. Sie müssen den DataContext nicht explizit für die Ansicht in der Vorlage festlegen. Es wird den DataContext der Datenplatte erben. Was bedeutet 'DataContext =" {Binding VM: CustomerVM} "? Versuchen Sie, es zu typisieren? –

+0

Danke für das Update. Woher bezieht das ContentControl seinen 'Content'? –

+0

Ich habe die Frage aktualisiert, wie die Ansicht geladen wird. Das 'CustomerVM' ist das ViewModel für die Kunden-Benutzersteuerungsansicht. – Bish25

Antwort

4

Ich denke, es seve sind Es geht hier rum. Wir beginnen mit demjenigen, der mir am Anfang am deutlichsten ist.

Ein DataContext ist ein Objekt - in der Regel eine Instanz einer Viewmodel-Klasse. Ein DataTemplate wird verwendet, um eine Instanz einer Viewmodel-Klasse anzuzeigen. Sein DataContext wird von den Steuerelementen geerbt, die es enthält. Dies ist wahrscheinlich das, was Sie wollen:

<Window.Resources> 
    <DataTemplate DataType="{x:Type VM:CustomerVM}"> 
     <View:Customers /> 
    </DataTemplate> 

    <DataTemplate DataType="{x:Type VM:SuppliersVM}"> 
     <View:Suppliers /> 
    </DataTemplate> 
</Window.Resources> 

Zweitens müssen Sie Ihren Content mit einem tatsächlichen Fall eines Kind Ansichtsmodell oder einem anderen bevölkern.

Eine Möglichkeit, Sie gefunden haben, ist jeweils ein <DataContext> Element in der XAML-Ansicht zu geben, die eine Viewmodel-Instanz erstellt:

<DataContext> 
    <VM:CustomerVM /> 
</DataContext> 

Aber wie ist das Hauptansichtsmodell mit seinen Kindern zu interagieren? Eine Lösung besteht darin, ein MVVM-Framework zu verwenden, das einen byzantinischen "Locator" hinzufügt, der das Problem auf halbem Wege mit einer Unordnung von Kludges und Workarounds löst. Much effort is spent satisfying the framework's demands.

Dieses Design legt die Ansichten für alles, aber Ansichten haben keine Logik. Sie haben keine Geschäfte in der Hand. Das "Gehirn" Ihrer Anwendung ist in ein Dutzend kleiner Teile aufgeteilt, die sich gegenseitig verstecken.

Der andere Weg ist "Viewmodel Centric Design": Entwerfen Sie Ihre Anwendung als eine Reihe von Viewmodels. Untergeordnete Ansichtsmodelle sind Eigenschaften von übergeordneten Ansichtsmodellen. MainVM kann leicht sein Kind finden SupplierVM, weil MainVM es erstellt und besitzt es. Anstelle der Ansichtsmodelle, die von den Ansichten herabhängen, sind die Ansichten vereinzelte Anhängsel der Ansichtsmodelle - was völlig in Ordnung ist, weil die Ansichten ziemlich einfach nur dort sitzen.

Alles mit WPF ist einfacher auf diese Weise.

Sie haben drei Viewmodels hier: Ein Elternteil, MainVM, und zwei Kinder, CustomerVM und SupplierVM. In der Hauptansicht möchten Sie jeweils ein Kind anzeigen.

Also werden wir MainVM eine Instanz jedes Kind geben:

public MainVM() 
{ 
    Customer = new CustomerVM(); 
    Supplier = new SupplierVM(); 
} 

public CustomerVM Customer { 
    get { return _customerVM; } 
    private set { _customerVM = value; } 
} 
public SupplierVM Supplier { 
    get { return _supplierVM; } 
    private set { _supplierVM = value; } 
} 

public INotifyPropertyChanged _selectedChild; 
public INotifyPropertyChanged SelectedChild { 
    get { return _selectedChild; } 
    set { 
     if (value != _selectedChild) { 
      _selectedChild = value; 
      // I don't know how you raise PropertyChanged; if it doesn't look 
      // like this, let me know. 
      OnPropertyChanged(); 
     } 
    } 
} 

public void Navigate() 
{   
    SelectedChild = Customer; 
} 

Im XAML wird die Inhaltskontrolle stark vereinfacht. Was auch immer Sie in SelectedChild eingeben, wird in der Inhaltssteuerung angezeigt. Woher weiß es, welche Vorlage zu verwenden ist?

Einfach: Wir haben das Attribut x:Key aus den obigen Datenvorlagen entfernt, aber wir haben die Attribute DataType verlassen. Das macht sie zu sogenannten "impliziten Datenvorlagen": Wo immer sie sich befinden, werden sie automatisch von einer beliebigen ContentControl verwendet, die ein Objekt eines dieser Typen anzeigen muss. Überall in diesem Window, wenn Sie XAML sagen "Hier ist ein CustomerVM; zeigen Sie es für den Benutzer", XAML wird die erste der beiden oben genannten Datentypen verwenden, um es anzuzeigen.

<ContentControl 
    Content="{Binding SelectedChild}" 
    Margin="0,135,0,10" 
    Grid.Column="1" 
    /> 

Sie können der SwitchView Eigenschaft loszuwerden. Was Sie getan haben, funktioniert sicherlich (und wie Sie gefunden haben, ist etwas, das in XAML funktioniert, nicht immer einfach), aber dieser Weg ist einfacher und konventioneller.

+1

Ich habe das jetzt umgesetzt, funktioniert ein Traum und half mir, MVVM ein bisschen mehr zu verstehen, nochmals vielen Dank. Wenn Sie irgendwelche Blogs oder Tutorials online haben, würde ich interessiert sein, wie Ihr Ansatz und Erklärung klar ist. – Bish25

Verwandte Themen