如何将来自两个不同嵌套对象的数据绑定到 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 id
、name
和 tipo
以及证明人 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>
您的代码存在一些问题:
- 需要修复:您只能绑定到 public 属性而不是字段。这意味着
insumos
必须是 public 属性。此外,绑定源(在本例中为 InsumosViewModel
和 InsumoVM
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);
}
}
- 需要修复:您必须使用正确的
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>
- 从不 return
void
来自 async
方法(除非此方法是事件处理程序)。 async
方法必须 return Task
或 Task<T>
:
public async Task InitializeViewModel()
{}
此外,在使用 MVVM 时,您不应将数据直接传递给视图。例如,不是将 List<Cliente>
传递给 ClientesView
,而是让 ClientesView
将其控件像 dgClients
绑定到 DataContext
上的 属性 / 查看模型 class。 List<Cliente>
必须是视图模型 class 的 属性。
要像 DataGrid
一样过滤 ItemsControl
的数据,请使用集合视图。不要为此创建分配给 ItemsSource 的新集合。很快就会变得很难维护。
dgClientes.Items.Filter = item => (items as Cliente).Apellidos.ToLower().Contains(txtFilter.Text.ToLower());
我目前有一个视图,它应该显示来自两个不同对象的数据。为此,我创建了一个视图模型,其中包含一个对象类型列表,该对象类型包含前面提到的两个不同对象。这两个对象是供应商和项目。 项目 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 id
、name
和 tipo
以及证明人 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>
您的代码存在一些问题:
- 需要修复:您只能绑定到 public 属性而不是字段。这意味着
insumos
必须是 public 属性。此外,绑定源(在本例中为InsumosViewModel
和InsumoVM
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);
}
}
- 需要修复:您必须使用正确的
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>
- 从不 return
void
来自async
方法(除非此方法是事件处理程序)。async
方法必须 returnTask
或Task<T>
:
public async Task InitializeViewModel()
{}
此外,在使用 MVVM 时,您不应将数据直接传递给视图。例如,不是将
List<Cliente>
传递给ClientesView
,而是让ClientesView
将其控件像dgClients
绑定到DataContext
上的 属性 / 查看模型 class。List<Cliente>
必须是视图模型 class 的 属性。要像
DataGrid
一样过滤ItemsControl
的数据,请使用集合视图。不要为此创建分配给 ItemsSource 的新集合。很快就会变得很难维护。
dgClientes.Items.Filter = item => (items as Cliente).Apellidos.ToLower().Contains(txtFilter.Text.ToLower());