【问题标题】:WPF - How to bind ICollectionView to datagrids using MVVMWPF - 如何使用 MVVM 将 ICollectionView 绑定到数据网格
【发布时间】:2016-04-27 20:03:56
【问题描述】:

我是 WPF 新手,并按照此链接使用代码优先方法来构建示例。这个例子有效。 https://msdn.microsoft.com/en-us/data/jj574514.aspx

现在,我正在尝试将其更改为遵循 MVVM。

这是主窗口 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>

这里是 MainWindowViewModel

 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();
    }
}

我不知道如何将第二个细节数据网格绑定到 ViewModel。我想要和原来的例子一样的Master-Details显示功能。

那么如何将Categories 中的Products 绑定到第二个datagrid 上呢?使用 MVVM 实现它的正确方法是什么?


更多信息:

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

【问题讨论】:

    标签: c# wpf mvvm datagrid icollectionview


    【解决方案1】:

    将主 DataGrid 的 SelectedItem 属性绑定到 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>
    

    主窗口视图模型

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

    (这需要您的视图模型实现 INotifyPropertyChanged。OnPropertyChanged 方法调用 PropertyChanged 事件处理程序)

    添加另一个返回所选类别的产品属性的属性

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

    将详细信息 DataGrid 绑定到视图模型中的 SelectedCategoryProducts 属性

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

    【讨论】:

    • 我在你的代码中改变了两件事,它起作用了。 1、SelectedCategoryProducts的类型应该是ObservableCollection而不是Category 2、details datagird绑定改为ItemsSource="{Binding SelectedCategoryProducts}"而不是DataContext="{Binding SelectedCategoryProducts, ElementName=MasterGrid}"
    • 原来的例子是使用拖放数据源的方法来绑定两个主从数据网格,非常简单。当我们将其移至 MVVM 时,似乎并不容易,必须绑定到公共属性。
    【解决方案2】:

    最简单的方法是将detail DataGrid的DataContext绑定到主DataGrid的SelectedItem属性

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

    【讨论】:

    • 嗨,你能检查一下上面关于类定义的更新吗?我正在尝试将产品绑定到我的第二个详细信息表。绑定 SelectedItem 对我不起作用。
    • @AppleJuice 将绑定路径更改为SelectedItem.Products 有效吗?
    • 不,SelectedItem.Products 不起作用。第二个详细信息数据网格中没有显示任何内容。
    • 好的,我将在单独的答案中展示另一种方法
    【解决方案3】:

    有了ICollectionView,不使用它的功能将是一种耻辱......

    首先,在您的第一个数据网格(类别)上设置IsSynchronizedToCurrentItem="true"

    然后在您的第二个 DataGrid 中,将 DataSource 与 ItemsSource="{Binding Categories.CurrentItem.Products}" 绑定,其中 Categories 是您的视图模型 ICollectionView

    IsSynchToCurrentItem=true 的效果是您不需要在视图模型中保存属性来跟踪当前项目,因为ICollectionView 会为您完成。

    然后每次用户在数据网格中选择一行时,当前项目将在视图模型中更改(并且ICollectionView 上有一个事件来通知)等每次您将在视图模型中设置当前项目,将选择相应的行。

    除此功能外,ICollectionView 使您能够在不触及源集合的情况下进行排序、过滤和分组,最重要的是,它使您能够以编程方式更改当前项目使用MoveCurrentTo(object target)MovecurrentToFirst() 等方法的 XAML 控件(以及相应的可视化/XAML 项目控件中的选定行)......

    你的业务 C# 模型很好,所以你的 XAML 看起来像这样:

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-05-28
      • 1970-01-01
      • 2013-04-15
      • 1970-01-01
      • 1970-01-01
      • 2013-12-03
      • 1970-01-01
      相关资源
      最近更新 更多