如何将来自两个不同嵌套对象的数据绑定到 DataGrid

How to bind data from two different nested objects to a DataGrid

我目前有一个视图,它应该显示来自两个不同对象的数据。为此,我创建了一个视图模型,其中包含一个对象类型列表,该对象类型包含前面提到的两个不同对象。这两个对象是供应商和项目。 项目 class

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Text.Json.Serialization;


namespace MLaRealERP.Models
{
    public class Insumo
    {
        [JsonPropertyName("Id")]
        public int Id { get; set; }

        [JsonPropertyName("proveedorId")]
        public int ProveedorId { get; set; }

        [JsonPropertyName("name")]
        public string Name { get; set; }

        [JsonPropertyName("tipo")]
        public string Tipo { get; set; }

        [JsonPropertyName("unidad")]
        public string Unidad { get; set; }

        [JsonPropertyName("precio")]
        public float Precio { get; set; }
    }
}

供应商Class

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Text.Json.Serialization;


namespace MLaRealERP.Models
{
    public class Proveedor
    {
        [JsonPropertyName("id")]
        public int Id { get; set; }

        [JsonPropertyName("name")]
        public string Name { get; set; }

        [JsonPropertyName("celular")]
        public string Celular { get; set; }

        public void NuevoProveedor(string n, string c)
        {
            Name = n;
            Celular = c;
        }
    }
}

视图模型

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using MLaRealERP.Models;

namespace MLaRealERP.ViewModels
{
    public class InsumosViewModel
    {
        public class InsumoVM
        {
            public Insumo insumo { get; set; }
            public Proveedor proveedor { get; set; }

        }
        List<Insumo> repo;
        List<Proveedor> proveedores;
        public List<InsumoVM> insumos;
        //IOrderedEnumerable<Proveedor> proveedores;
        public InsumosViewModel()
        {
            repo = new List<Insumo>();
            proveedores = new List<Proveedor>();
            insumos = new List<InsumoVM>();
        }
        public async void InitializeViewModel()
        {
            repo = await Funciones.GetAll<Insumo>("insumos", App.client);
            proveedores = await Funciones.GetAll<Proveedor>("proveedors", App.client);
            proveedores = proveedores.OrderBy(p => p.Id).ToList();
            

            //proveedores = repo.OrderBy(repo => repo.Id);
            foreach(Insumo insumo in repo)
            {
                newInsumoVM(insumo);
            }
        }
        private void newInsumoVM(Insumo ins)
        {
            InsumoVM insumoVM = new InsumoVM();
            insumoVM.insumo = ins;
            insumoVM.proveedor = proveedores[ins.ProveedorId - 1];
            insumos.Add(insumoVM);
        }

    }
}

数据网格Xaml

<Window x:Class="MLaRealERP.Views.InsumosView"
        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:MLaRealERP.Views"
        mc:Ignorable="d"
        Title="InsumosView" Height="450" Width="800">
    <DockPanel Margin="10">
        <Canvas DockPanel.Dock="Top" Margin="0,0,0,20">
            <TextBox  Canvas.Left="10"  Name="txtFilter" TextChanged="txtFilter_TextChanged" Width="171" />
            <Button  Canvas.Right="10" Name="btnAddInsumo" Click="btnAddInsumo_Click">Agregar Nuevo Producto</Button>
        </Canvas>
        <DataGrid ItemsSource="{Binding insumos}" Name="dgInsumos" Margin="10" ColumnWidth="*" CanUserAddRows="False" >
            <DataGrid.ColumnHeaderStyle>
                <Style TargetType="DataGridColumnHeader">
                    <Setter Property="FontWeight" Value="Bold" />
                    <Setter Property="Background" Value="LightBlue" />
                    <Setter Property="BorderBrush" Value="Black" />
                    <Setter Property="BorderThickness" Value="0.5" />
                </Style>
            </DataGrid.ColumnHeaderStyle>
            <DataGrid.Columns>
                <DataGridTextColumn Header="Id" Binding="{Binding insumos.insumo.Id}" />
                <DataGridTextColumn Header="Nombre" Binding="{Binding insumos.insumo.Name}" />
                <DataGridTextColumn Header="Tipo" Binding="{Binding insumos.insumo.Tipo}" />
            </DataGrid.Columns>
        </DataGrid>
    </DockPanel>
</Window>

Datagrid CS

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using MLaRealERP.ViewModels;

namespace MLaRealERP.Views
{
    /// <summary>
    /// Interaction logic for InsumosView.xaml
    /// </summary>
    public partial class InsumosView : Window
    {
        InsumosViewModel insumos;
        public InsumosView()
        {
            InitializeComponent();
            insumos = new InsumosViewModel();
            insumos.InitializeViewModel();
            dgInsumos.DataContext = insumos;
        }

        //public async void LoadViewItems()
        //{
        //    insumos.InitializeViewModel();
        //}
        private void txtFilter_TextChanged(object sender, TextChangedEventArgs e)
        {

        }

        private void btnAddInsumo_Click(object sender, RoutedEventArgs e)
        {

        }
    }
}

我想展示 Isumos idnametipo 以及证明人 name

编辑: 当前 View.xaml 为网格

<Window x:Class="MLaRealERP.Views.InsumosView"
        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:MLaRealERP.Views"
        mc:Ignorable="d"
        Title="InsumosView" Height="450" Width="800">
    <DockPanel Margin="10">
        <Canvas DockPanel.Dock="Top" Margin="0,0,0,20">
            <TextBox  Canvas.Left="10"  Name="txtFilter" TextChanged="txtFilter_TextChanged" Width="171" />
            <Button  Canvas.Right="10" Name="btnAddInsumo" Click="btnAddInsumo_Click">Agregar Nuevo Producto</Button>
        </Canvas>
        <DataGrid ItemsSource="{Binding insumos}" DataContext="{Binding InsumoVM}" Name="dgInsumos" Margin="10" ColumnWidth="*" CanUserAddRows="False" >
            <DataGrid.ColumnHeaderStyle>
                <Style TargetType="DataGridColumnHeader">
                    <Setter Property="FontWeight" Value="Bold" />
                    <Setter Property="Background" Value="LightBlue" />
                    <Setter Property="BorderBrush" Value="Black" />
                    <Setter Property="BorderThickness" Value="0.5" />
                </Style>
            </DataGrid.ColumnHeaderStyle>
            <DataGrid.Columns>
                <DataGridTextColumn Header="Id" Binding="{Binding insumo.Id}" />
                <DataGridTextColumn Header="Nombre" Binding="{Binding insumo.Name}" />
                <DataGridTextColumn Header="Tipo" Binding="{Binding insumo.Tipo}" />

            </DataGrid.Columns>
        </DataGrid>
    </DockPanel>
</Window>

您的代码存在一些问题:

  1. 需要修复:您只能绑定到 public 属性而不是字段。这意味着 insumos 必须是 public 属性。此外,绑定源(在本例中为 InsumosViewModelInsumoVM classes)必须实现 INotifyPropertyChanged(完整的 example 参见 Microsoft 文档)——即使您不希望 属性 发生变化。

InsumoVM.cs

public class InsumoVM : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    public Insumo Insumo { get; set; }
    public Proveedor Proveedor { get; set; }
}

InsumosViewModel.cs

public class InsumosViewModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    public ObservableCollection<InsumoVM> Insumos { get; }
    private List<Insumo> Repo { get; set; }
    private List<Proveedor> Proveedores { get; set; }

    public InsumosViewModel()
    {
        this.Repo = new List<Insumo>();
        this.Proveedores = new List<Proveedor>();
        this.Insumos = new ObservableCollection<InsumoVM>();
    }

    public async Task InitializeViewModel()
    {
        this.Repo = await Funciones.GetAll<Insumo>("insumos", App.client);
        this.Proveedores = (await Funciones.GetAll<Proveedor>("proveedors", App.client)).
          OrderBy(p => p.Id)
          .ToList();
        
        this.Insumos.Clear();
        foreach(Insumo insumo in this.Repo)
        {
            NewInsumoVM(insumo);
        }
    }

    private void NewInsumoVM(Insumo ins)
    {
        InsumoVM insumoVM = new InsumoVM()
        {
          Insumo = ins,
          Proveedor = proveedores[ins.ProveedorId - 1]
        };
        this.Insumos.Add(insumoVM);
    }
}
  1. 需要修复:您必须使用正确的 Binding.Path。这些项目的类型为 InsumoVM。因此InsumoVM是每列DataContext的类型:
<DataGrid ItemsSource="{Binding Insumos}">
  <DataGridTextColumn Header="Insumo Name" 
                      Binding="{Binding Insumo.Name}" />
  <DataGridTextColumn Header="Proveedor Name" 
                      Binding="{Binding Proveedor.Name}" />
</DataGrid>
  1. 从不 return void 来自 async 方法(除非此方法是事件处理程序)。 async 方法必须 return TaskTask<T>:
public async Task InitializeViewModel()
{}
  1. 此外,在使用 MVVM 时,您不应将数据直接传递给视图。例如,不是将 List<Cliente> 传递给 ClientesView,而是让 ClientesView 将其控件像 dgClients 绑定到 DataContext 上的 属性 / 查看模型 class。 List<Cliente> 必须是视图模型 class 的 属性。

  2. 要像 DataGrid 一样过滤 ItemsControl 的数据,请使用集合视图。不要为此创建分配给 ItemsSource 的新集合。很快就会变得很难维护。

dgClientes.Items.Filter = item => (items as Cliente).Apellidos.ToLower().Contains(txtFilter.Text.ToLower());