2016-04-27 16 views
0

Ich bin neu bei WPF und folge diesem Link, um Code-Methode zu verwenden, um das Beispiel zu erstellen. Und das Beispiel funktioniert. https://msdn.microsoft.com/en-us/data/jj574514.aspxWPF - So binden Sie ICollectionView mit MVRM an Datenagenten

Jetzt versuche ich es zu ändern MVVM zu folgen. Hier

ist das Hauptfenster XAML

<Window 
    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:WPFwithEFSampleCodeFirst" mc:Ignorable="d" x:Class="WPFwithEFSampleCodeFirst.MainWindow" 
    Title="MainWindow" Height="352.134" Width="517.53" Loaded="Window_Loaded"> 

<Grid Margin="0,0,0,-3"> 
    <Grid.ColumnDefinitions> 
     <ColumnDefinition Width="0*"/> 
     <ColumnDefinition Width="77*"/> 
     <ColumnDefinition Width="25*"/> 
    </Grid.ColumnDefinitions> 
    <Button Content="Save" Grid.Column="2" HorizontalAlignment="Left" Margin="41,167,0,0" VerticalAlignment="Top" Width="75" Click="Button_Click"/> 
    <DataGrid Grid.ColumnSpan="2" ItemsSource="{Binding Categories}" AutoGenerateColumns="False" HorizontalAlignment="Left" Margin="32,10,0,0" VerticalAlignment="Top" Height="124" Width="330" > 
     <DataGrid.Columns> 
      <DataGridTextColumn Width="SizeToHeader" Header="Category Id" Binding="{Binding Path = CategoryId}"/> 
      <DataGridTextColumn Width="SizeToHeader" Header="Name" Binding="{Binding Path = Name}"/> 
     </DataGrid.Columns> 
    </DataGrid> 

    <DataGrid Grid.ColumnSpan="2" AutoGenerateColumns="False" HorizontalAlignment="Left" Margin="32,153,0,0" VerticalAlignment="Top" Height="146" Width="330"> 
     <DataGrid.Columns> 
      <DataGridTextColumn Binding="{Binding CategoryId}" Header="Category Id" Width="SizeToHeader"/> 
      <DataGridTextColumn Binding="{Binding Name}" Header="Name" Width="SizeToHeader"/> 
      <DataGridTextColumn Binding="{Binding ProductId}" Header="Product Id" Width="SizeToHeader"/> 
     </DataGrid.Columns> 
    </DataGrid> 
</Grid> 

Hier die MainWindowViewModel ist

class MainWindowViewModel 
{ 
    private ICollectionView _categoryView; 

    public ICollectionView Categories 
    { 
     get { return _categoryView; } 
    } 

    ProductContext context = new ProductContext(); 

    public MainWindowViewModel() 
    { 
     IList<Category> categories = GetCategories(); 
     _categoryView = CollectionViewSource.GetDefaultView(categories); 

    } 

    public IList<Category> GetCategories() 
    { 
     return context.Categories.ToList(); 
    } 
} 

Ich weiß nicht, wie die zweiten Details Datagrid Ansichtsmodell zu binden. Ich möchte die gleiche Master-Details-Anzeigefunktion wie das ursprüngliche Beispiel haben.

So, wie Produkte in Kategorien an das zweite Datagrid gebunden werden? Was ist der richtige Weg, um MVVM zu implementieren?


Weitere Informationen:

public class Category 
{ 
    public Category() 
    { 
     this.Products = new ObservableCollection<Product>(); 
    } 

    public int CategoryId { get; set; } 
    public string Name { get; set; } 

    public virtual ObservableCollection<Product> Products { get; private set; } 
} 

    public class Product 
{ 
    public int ProductId { get; set; } 
    public string Name { get; set; } 

    public int CategoryId { get; set; } 
    public virtual Category Category { get; set; } 
} 

    public class ProductContext : DbContext 
{ 
    public DbSet<Category> Categories { get; set; } 
    public DbSet<Product> Products { get; set; } 
} 

Antwort

0

Bind die Master-Datagrid SelectedItem-Eigenschaft zu binden eine Eigenschaft im ViewModel

<DataGrid SelectedItem="{Binding SelectedCategory}" Grid.ColumnSpan="2" ItemsSource="{Binding Categories}" AutoGenerateColumns="False"> 
    <DataGrid.Columns> 
     <DataGridTextColumn Width="SizeToHeader" Header="Category Id" Binding="{Binding Path = CategoryId}"/> 
     <DataGridTextColumn Width="SizeToHeader" Header="Name" Binding="{Binding Path = Name}"/> 
    </DataGrid.Columns> 
</DataGrid> 

MainWindowViewModel

private Category _selectedCategory; 

public Category SelectedCategory 
{ 
    get { return _selectedCategory; } 
    set 
    { 
     _selectedCategory = value; 
     OnPropertyChanged("SelectedCategory"); 
     OnPropertyChanged("SelectedCategoryProducts"); 
    } 
} 

(dies erfordert Ihre Ansicht Modell INotifyPropertyChanged umzusetzen. Die OnPropertyChanged Methode ruft die Property Event-Handler)

eine andere Eigenschaft hinzufügen, dass die gewählte Kategorie der Produkte Eigenschaft in der View-Modell

public ObservableCollection<Product> SelectedCategoryProducts 
{ 
    get 
    { 
     if (_selectedCategory == null) return null; 

     return _selectedCategory.Products; 
    } 
} 

Binden Sie die Details Datagrid an das SelectedCategoryProducts Eigenschaft gibt

<DataGrid ItemsSource="{Binding SelectedCategoryProducts}" Grid.ColumnSpan="2" AutoGenerateColumns="False"> 
    <DataGrid.Columns> 
     <DataGridTextColumn Binding="{Binding CategoryId}" Header="Category Id" Width="SizeToHeader"/> 
     <DataGridTextColumn Binding="{Binding Name}" Header="Name" Width="SizeToHeader"/> 
     <DataGridTextColumn Binding="{Binding ProductId}" Header="Product Id" Width="SizeToHeader"/> 
    </DataGrid.Columns> 
</DataGrid> 
+0

Ich habe zwei Dinge in Ihrem Code geändert und es hat funktioniert. 1. Der Typ von SelectedCategoryProducts sollte ObservableCollection anstelle von Kategorie 2 sein. Die Details der Datagird-Bindung werden geändert in ItemsSource = "{Binding SelectedCategoryProducts}" anstelle von DataContext = "{Binding SelectedCategoryProducts, ElementName = MasterGrid}" –

+0

Das ursprüngliche Beispiel verwendet Drag-and-Drop-Datenquellen Methode, um die beiden Master-Details Datagrids zu binden, die sehr einfach ist. Wenn wir es auf MVVM verschieben, scheint es keinen einfachen Weg zu geben und an öffentliche Objekte zu binden. –

0

einfachste Weg ist die Datacontext des Detaildatenraster auf die SelectedItem-Eigenschaft des Master-Datagrid

<DataGrid x:Name="MasterGrid" Grid.ColumnSpan="2" ItemsSource="{Binding Categories}" AutoGenerateColumns="False"> 
    <DataGrid.Columns> 
     <DataGridTextColumn Width="SizeToHeader" Header="Category Id" Binding="{Binding Path = CategoryId}"/> 
     <DataGridTextColumn Width="SizeToHeader" Header="Name" Binding="{Binding Path = Name}"/> 
    </DataGrid.Columns> 
</DataGrid> 

<DataGrid DataContext="{Binding SelectedItem.Products, ElementName=MasterGrid}" Grid.ColumnSpan="2" AutoGenerateColumns="False"> 
    <DataGrid.Columns> 
     <DataGridTextColumn Binding="{Binding CategoryId}" Header="Category Id" Width="SizeToHeader"/> 
     <DataGridTextColumn Binding="{Binding Name}" Header="Name" Width="SizeToHeader"/> 
     <DataGridTextColumn Binding="{Binding ProductId}" Header="Product Id" Width="SizeToHeader"/> 
    </DataGrid.Columns> 
</DataGrid> 
+0

Hallo, können Sie das obige Update bezüglich der Klassendefinition überprüfen? Ich versuche, Produkte an meine zweite Detailtabelle zu binden. Binding SelectedItem funktioniert nicht für mich. –

+0

@AppleJuice Kann der Bindungspfad zu SelectedItem.Products geändert werden? –

+0

Nein, SelectedItem.Products funktioniert nicht. Nichts zeigt in den zweiten Details Datagrid. –

0

Mit a ICollectionView wäre es schade, nicht seine Funktionalitäten zu nutzen ...

Zuerst se t die IsSynchronizedToCurrentItem="true" auf Ihrem ersten Datagrid (Kategorien).

Dann binden Sie in Ihrem zweiten DataGrid die DataSource mit ItemsSource="{Binding Categories.CurrentItem.Products}" wo Kategorien ist Ihr Ansichtsmodell ICollectionView.

Der Effekt von IsSynchToCurrentItem=true ist, dass Sie keine Eigenschaft in Ihrem Viewmodel halten müssen, um Ihren aktuellen Artikel zu verfolgen, weil das ICollectionView das für Sie tut.

Dann jedes Mal, wenn der Benutzer eine Zeile im Datagrid auswählt, ändert sich das aktuelle Element im Ansichtsmodell (und es gibt ein Ereignis auf dem ICollectionView zu benachrichtigen) und jedes Mal, wenn Sie das aktuelle Element in Ihrem Ansichtsmodell festlegen, Die entsprechende Zeile wird ausgewählt.

Zusätzlich zu dieser Funktion ermöglicht die ICollectionView Sie zu sortieren, filtern und Gruppe ohne Ihre Quellensammlung zu berühren und vor allem ermöglicht es Ihnen, programmatisch das aktuelle Element ändern IN SIE MODEL ansehen, ohne MESSING ARROUND MIT DER XAML STEUER (und damit die ausgewählte Zeile in dem entsprechenden visuellen/XAML ItemControl) unter Verwendung des Verfahren wie MoveCurrentTo(object target), MovecurrentToFirst(), und so weiter ....

Ihr Unternehmen C# Modell ist in Ordnung, so dass Ihr XAML aussehen würde, so:

<DataGrid Grid.ColumnSpan="2" IsSynchronizedToCurrentItem="true" ItemsSource="{Binding Categories}" AutoGenerateColumns="False" HorizontalAlignment="Left" Margin="32,10,0,0" VerticalAlignment="Top" Height="124" Width="330" > 
    <DataGrid.Columns> 
     <DataGridTextColumn Width="SizeToHeader" Header="Category Id" Binding="{Binding Path = CategoryId}"/> 
     <DataGridTextColumn Width="SizeToHeader" Header="Name" Binding="{Binding Path = Name}"/> 
    </DataGrid.Columns> 
</DataGrid> 

<DataGrid Grid.ColumnSpan="2" ItemsSource="{Binding Categories.CurrentItem.Products}" AutoGenerateColumns="False" HorizontalAlignment="Left" Margin="32,153,0,0" VerticalAlignment="Top" Height="146" Width="330"> 
    <DataGrid.Columns> 
     <DataGridTextColumn Binding="{Binding CategoryId}" Header="Category Id" Width="SizeToHeader"/> 
     <DataGridTextColumn Binding="{Binding Name}" Header="Name" Width="SizeToHeader"/> 
     <DataGridTextColumn Binding="{Binding ProductId}" Header="Product Id" Width="SizeToHeader"/> 
    </DataGrid.Columns> 
</DataGrid> 
Verwandte Themen