如何在 DataGrid RowDetailsTemplate 中显示 DataGrid?
How to display DataGrid inside DataGrid RowDetailsTemplate?
我想在另一个 DataGrid
行详细信息模板中添加一行 DataGrid
。
这是我的 XAML:
<DataGrid x:Name="dataGrid" Margin="10,100,10,96" PreviewKeyDown="dataGrid_PreviewKeyDown" AutoGenerateColumns="False" ItemsSource="{Binding }" AlternatingRowBackground="AliceBlue" CellEditEnding="onCellEditEnding" SelectedItem="{Binding SelectedItem}" >
<DataGrid.Columns>
<DataGridTextColumn Width="*" Header="No" Binding= "{Binding Path=Id, Mode=TwoWay}" IsReadOnly="True" />
<DataGridTextColumn Width="*" Header="Barcode" Binding= "{Binding Path=Barcode, Mode=TwoWay}" />
<DataGridTextColumn Width="*" Header="Item" Binding= "{Binding Path=Item, Mode=TwoWay}" IsReadOnly="True"/>
<DataGridTextColumn Width="*" Header="Qty" Binding= "{Binding Path=Qty, Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/>
<DataGridTextColumn Width="*" Header="Mrp" Binding= "{Binding Path=Mrp, Mode=TwoWay}" IsReadOnly="True"/>
<DataGridTextColumn Width="*" Header="Discount" Binding= "{Binding Path=Discount, Mode=TwoWay}" IsReadOnly="True"/>
<DataGridTextColumn Width="*" Header="Cgst" Binding= "{Binding Path=Cgst, Mode=TwoWay}" IsReadOnly="True"/>
<DataGridTextColumn Width="*" Header="Sgst" Binding= "{Binding Path=Sgst, Mode=TwoWay}" IsReadOnly="True"/>
<DataGridTextColumn Width="*" Header="Amount" Binding= "{Binding Path=Amount, Mode=OneWay ,UpdateSourceTrigger=PropertyChanged}" IsReadOnly="True"/>
<DataGridTextColumn Width="*" Header="Salesman" Binding= "{Binding Path=Salesman, Mode=TwoWay}" IsReadOnly="True"/>
<DataGridTemplateColumn Header="Delete">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button Content="X" Click="Button_Click"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
<DataGrid.RowDetailsTemplate >
<DataTemplate>
<DataGrid x:Name="RowDetailsDataGrid" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Width="*" Binding="{Binding Details}" />
<DataGridTextColumn Width="*" Binding="{Binding CgstDetails}" />
<DataGridTextColumn Width="*" Binding="{Binding SgstDetails}" />
<DataGridTextColumn Width="*" Binding="{Binding DiscountDetails}"/>
</DataGrid.Columns>
</DataGrid>
</DataTemplate>
</DataGrid.RowDetailsTemplate>
</DataGrid>
这是(大致)我的代码隐藏:
ObservableCollection<TableData> tableData = new ObservableCollection<TableData>();
public MainWindow()
{
InitializeComponent();
dataGrid.DataContext = tableData;
}
public class TableData : INotifyPropertyChanged
{
public int Id { get; set; }
public int No { get; set; }
public string Details { get; set; }
public string CgstDetails { get; set; }
public string SgstDetails { get; set; }
public string DiscountDetails { get; set; }
public string Barcode { get; set; }
public string Item { get; set; }
private double qty;
public double Qty {
get { return qty; }
set
{
qty = value;
OnPropertyChanged("Qty");
}
}
public double Mrp { get; set; }
public double Discount { get; set; }
public double Cgst { get; set; }
public double Sgst { get; set; }
private double amount; // field
public double Amount
{
get { return amount; }
set {
amount = (Qty * Mrp) + ((Cgst / 100) * (Mrp * Qty)) + ((Sgst / 100) * (Mrp * Qty));
OnPropertyChanged("Amount");
}
}
public int Salesman { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(String propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
public bool RowAdded{ get; set; }
}
private void onCellEditEnding(object sender, DataGridCellEditEndingEventArgs e)
{
var rowFetched = new TableData()
{
Id = currentRowIndex + 1,
No = currentRowIndex + 1,
Barcode = item.Barcode,
Qty = item.Qty,
Mrp = item.Mrp,
Item = item.Item,
Discount = item.Discount,
Cgst = item.Cgst,
Sgst = item.Cgst,
Amount = item.Amount,
Salesman = item.Salesman,
Details = item.Details,
DiscountDetails = item.DiscountDetails,
CgstDetails = item.CgstDetails,
SgstDetails = item.SgstDetails,
RowAdded = true
};
tableData.Insert(currentRowIndex, rowFetched);
}
这是行不通的。我只是将 TextBlock
放在 WrapPanel
中而不是 DataGrid
(请参阅下面的代码)并且数据绑定工作正常并且数据显示在行详细信息中,但是我希望它是一行 (DataGrid
) 而不是 WrapPanel
.
<DataGrid.RowDetailsTemplate >
<DataTemplate>
<WrapPanel Background="LightGray">
<Border BorderBrush="Black" BorderThickness="1" Width="355" Padding="40 0 0 0" >
<TextBlock FontSize="16" Foreground="MidnightBlue" Text="{Binding Details}" VerticalAlignment="Center" />
</Border>
<Border BorderBrush="Black" BorderThickness="1" Width="71">
<TextBlock FontSize="16" Foreground="MidnightBlue" Text="{Binding CgstDetails}" VerticalAlignment="Center" />
</Border>
<Border BorderBrush="Black" BorderThickness="1" Width="71">
<TextBlock FontSize="16" Foreground="MidnightBlue" Text="{Binding SgstDetails}" VerticalAlignment="Center" />
</Border>
<Border BorderBrush="Black" BorderThickness="1" Width="71">
<TextBlock FontSize="16" Foreground="MidnightBlue" Text="{Binding DiscountDetails}" VerticalAlignment="Center" />
</Border>
<Border BorderBrush="Black" BorderThickness="1" Width="188">
<TextBlock FontSize="16" Foreground="MidnightBlue" VerticalAlignment="Center" />
</Border>
</WrapPanel>
</DataTemplate>
</DataGrid.RowDetailsTemplate>
有没有人可以帮忙?
我认为将嵌套 DataGrid
放入行详细信息模板中只是为了显示一行不是一个好主意,但如果这是你唯一的选择,你可以按如下方式进行.
A DataGrid
需要集合类型的 ItemsSource
,更具体地说是 IEnumerable
。您尝试做的是直接在 DataContext
本身上绑定属性,这是行不通的。您必须至少有两个选项才能使其工作,这两个选项本质上都提供了单个项目的集合,如 ItemsSource
到嵌套的 DataGrid
.
创建一个值转换器,将您的 TableData
项目包装在一个集合中(这里是一个数组)。
public class ItemToCollectionConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return value is null ? null : new[] { value };
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new InvalidOperationException();
}
}
在 XAML 中创建一个转换器实例(例如在您的 DataGrid.Resources
中)并简单地将使用转换器的数据上下文绑定到嵌套 DataGrid
的 ItemsSource
].通过这种方式,您的 TableData
项目被包裹在一个集合中并被视为单个项目集合,因此列绑定将正常工作。
<DataGrid x:Name="dataGrid" Margin="10,100,10,96" PreviewKeyDown="dataGrid_PreviewKeyDown" AutoGenerateColumns="False" ItemsSource="{Binding}" AlternatingRowBackground="AliceBlue" CellEditEnding="onCellEditEnding" SelectedItem="{Binding SelectedItem}" >
<DataGrid.Resources>
<local:ItemToCollectionConverter x:Key="ItemToCollectionConverter"/>
</DataGrid.Resources>
<DataGrid.Columns>
<DataGridTextColumn Width="*" Header="No" Binding= "{Binding Path=Id, Mode=TwoWay}" IsReadOnly="True" />
<DataGridTextColumn Width="*" Header="Barcode" Binding= "{Binding Path=Barcode, Mode=TwoWay}" />
<DataGridTextColumn Width="*" Header="Item" Binding= "{Binding Path=Item, Mode=TwoWay}" IsReadOnly="True"/>
<DataGridTextColumn Width="*" Header="Qty" Binding= "{Binding Path=Qty, Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/>
<DataGridTextColumn Width="*" Header="Mrp" Binding= "{Binding Path=Mrp, Mode=TwoWay}" IsReadOnly="True"/>
<DataGridTextColumn Width="*" Header="Discount" Binding= "{Binding Path=Discount, Mode=TwoWay}" IsReadOnly="True"/>
<DataGridTextColumn Width="*" Header="Cgst" Binding= "{Binding Path=Cgst, Mode=TwoWay}" IsReadOnly="True"/>
<DataGridTextColumn Width="*" Header="Sgst" Binding= "{Binding Path=Sgst, Mode=TwoWay}" IsReadOnly="True"/>
<DataGridTextColumn Width="*" Header="Amount" Binding= "{Binding Path=Amount, Mode=OneWay ,UpdateSourceTrigger=PropertyChanged}" IsReadOnly="True"/>
<DataGridTextColumn Width="*" Header="Salesman" Binding= "{Binding Path=Salesman, Mode=TwoWay}" IsReadOnly="True"/>
<DataGridTemplateColumn Header="Delete">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button Content="X" Click="Button_Click"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
<DataGrid.RowDetailsTemplate>
<DataTemplate>
<DataGrid x:Name="RowDetailsDataGrid" AutoGenerateColumns="False" ItemsSource="{Binding Converter={StaticResource ItemToCollectionConverter}}">
<DataGrid.Columns>
<DataGridTextColumn Width="*" Binding="{Binding Details}" />
<DataGridTextColumn Width="*" Binding="{Binding CgstDetails}" />
<DataGridTextColumn Width="*" Binding="{Binding SgstDetails}" />
<DataGridTextColumn Width="*" Binding="{Binding DiscountDetails}"/>
</DataGrid.Columns>
</DataGrid>
</DataTemplate>
</DataGrid.RowDetailsTemplate>
</DataGrid>
从您的 TableData
类型公开一个集合 属性,其中包含子类型或仅包含详细信息属性的辅助类型的单个项目。
public class TableDetailsData
{
public string Details { get; set; }
public string CgstDetails { get; set; }
public string SgstDetails { get; set; }
public string DiscountDetails { get; set; }
}
public class TableData : INotifyPropertyChanged
{
private TableDetailsData _detailsData;
public TableDetailsData DetailsData
{
get => _detailsData;
set
{
_detailsData = value;
DetailsDataCollection = new List<TableDetailsData> { _detailsData };
OnPropertyChanged(nameof(DetailsDataCollection));
}
}
public IEnumerable<TableDetailsData> DetailsDataCollection { get; private set; }
// ...other code.
}
DetailsDataCollection
始终在 DetailsData
更改时更新。无论您是像这样创建集合并将 DetailsData
公开为对象还是属性,都取决于您,有很多可能性,这只是为了了解要做什么。现在您可以在嵌套的 DataGrid
.
中将此集合绑定为 ItemsSource
<DataGrid x:Name="RowDetailsDataGrid" AutoGenerateColumns="False" ItemsSource="{Binding DetailsDataCollection}" CanUserAddRows="False">
就个人而言,我更喜欢值转换器解决方案,因为您不必公开冗余集合 属性 并污染您的代码,因为它不是一个非常干净的解决方案。我建议您创建一个适当的详细信息类型,并考虑一种比单行更好的方式来表示数据 DataGrid
,这似乎不合适并且在这种情况下不能提供良好的用户体验。
我想在另一个 DataGrid
行详细信息模板中添加一行 DataGrid
。
这是我的 XAML:
<DataGrid x:Name="dataGrid" Margin="10,100,10,96" PreviewKeyDown="dataGrid_PreviewKeyDown" AutoGenerateColumns="False" ItemsSource="{Binding }" AlternatingRowBackground="AliceBlue" CellEditEnding="onCellEditEnding" SelectedItem="{Binding SelectedItem}" >
<DataGrid.Columns>
<DataGridTextColumn Width="*" Header="No" Binding= "{Binding Path=Id, Mode=TwoWay}" IsReadOnly="True" />
<DataGridTextColumn Width="*" Header="Barcode" Binding= "{Binding Path=Barcode, Mode=TwoWay}" />
<DataGridTextColumn Width="*" Header="Item" Binding= "{Binding Path=Item, Mode=TwoWay}" IsReadOnly="True"/>
<DataGridTextColumn Width="*" Header="Qty" Binding= "{Binding Path=Qty, Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/>
<DataGridTextColumn Width="*" Header="Mrp" Binding= "{Binding Path=Mrp, Mode=TwoWay}" IsReadOnly="True"/>
<DataGridTextColumn Width="*" Header="Discount" Binding= "{Binding Path=Discount, Mode=TwoWay}" IsReadOnly="True"/>
<DataGridTextColumn Width="*" Header="Cgst" Binding= "{Binding Path=Cgst, Mode=TwoWay}" IsReadOnly="True"/>
<DataGridTextColumn Width="*" Header="Sgst" Binding= "{Binding Path=Sgst, Mode=TwoWay}" IsReadOnly="True"/>
<DataGridTextColumn Width="*" Header="Amount" Binding= "{Binding Path=Amount, Mode=OneWay ,UpdateSourceTrigger=PropertyChanged}" IsReadOnly="True"/>
<DataGridTextColumn Width="*" Header="Salesman" Binding= "{Binding Path=Salesman, Mode=TwoWay}" IsReadOnly="True"/>
<DataGridTemplateColumn Header="Delete">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button Content="X" Click="Button_Click"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
<DataGrid.RowDetailsTemplate >
<DataTemplate>
<DataGrid x:Name="RowDetailsDataGrid" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Width="*" Binding="{Binding Details}" />
<DataGridTextColumn Width="*" Binding="{Binding CgstDetails}" />
<DataGridTextColumn Width="*" Binding="{Binding SgstDetails}" />
<DataGridTextColumn Width="*" Binding="{Binding DiscountDetails}"/>
</DataGrid.Columns>
</DataGrid>
</DataTemplate>
</DataGrid.RowDetailsTemplate>
</DataGrid>
这是(大致)我的代码隐藏:
ObservableCollection<TableData> tableData = new ObservableCollection<TableData>();
public MainWindow()
{
InitializeComponent();
dataGrid.DataContext = tableData;
}
public class TableData : INotifyPropertyChanged
{
public int Id { get; set; }
public int No { get; set; }
public string Details { get; set; }
public string CgstDetails { get; set; }
public string SgstDetails { get; set; }
public string DiscountDetails { get; set; }
public string Barcode { get; set; }
public string Item { get; set; }
private double qty;
public double Qty {
get { return qty; }
set
{
qty = value;
OnPropertyChanged("Qty");
}
}
public double Mrp { get; set; }
public double Discount { get; set; }
public double Cgst { get; set; }
public double Sgst { get; set; }
private double amount; // field
public double Amount
{
get { return amount; }
set {
amount = (Qty * Mrp) + ((Cgst / 100) * (Mrp * Qty)) + ((Sgst / 100) * (Mrp * Qty));
OnPropertyChanged("Amount");
}
}
public int Salesman { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(String propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
public bool RowAdded{ get; set; }
}
private void onCellEditEnding(object sender, DataGridCellEditEndingEventArgs e)
{
var rowFetched = new TableData()
{
Id = currentRowIndex + 1,
No = currentRowIndex + 1,
Barcode = item.Barcode,
Qty = item.Qty,
Mrp = item.Mrp,
Item = item.Item,
Discount = item.Discount,
Cgst = item.Cgst,
Sgst = item.Cgst,
Amount = item.Amount,
Salesman = item.Salesman,
Details = item.Details,
DiscountDetails = item.DiscountDetails,
CgstDetails = item.CgstDetails,
SgstDetails = item.SgstDetails,
RowAdded = true
};
tableData.Insert(currentRowIndex, rowFetched);
}
这是行不通的。我只是将 TextBlock
放在 WrapPanel
中而不是 DataGrid
(请参阅下面的代码)并且数据绑定工作正常并且数据显示在行详细信息中,但是我希望它是一行 (DataGrid
) 而不是 WrapPanel
.
<DataGrid.RowDetailsTemplate >
<DataTemplate>
<WrapPanel Background="LightGray">
<Border BorderBrush="Black" BorderThickness="1" Width="355" Padding="40 0 0 0" >
<TextBlock FontSize="16" Foreground="MidnightBlue" Text="{Binding Details}" VerticalAlignment="Center" />
</Border>
<Border BorderBrush="Black" BorderThickness="1" Width="71">
<TextBlock FontSize="16" Foreground="MidnightBlue" Text="{Binding CgstDetails}" VerticalAlignment="Center" />
</Border>
<Border BorderBrush="Black" BorderThickness="1" Width="71">
<TextBlock FontSize="16" Foreground="MidnightBlue" Text="{Binding SgstDetails}" VerticalAlignment="Center" />
</Border>
<Border BorderBrush="Black" BorderThickness="1" Width="71">
<TextBlock FontSize="16" Foreground="MidnightBlue" Text="{Binding DiscountDetails}" VerticalAlignment="Center" />
</Border>
<Border BorderBrush="Black" BorderThickness="1" Width="188">
<TextBlock FontSize="16" Foreground="MidnightBlue" VerticalAlignment="Center" />
</Border>
</WrapPanel>
</DataTemplate>
</DataGrid.RowDetailsTemplate>
有没有人可以帮忙?
我认为将嵌套 DataGrid
放入行详细信息模板中只是为了显示一行不是一个好主意,但如果这是你唯一的选择,你可以按如下方式进行.
A DataGrid
需要集合类型的 ItemsSource
,更具体地说是 IEnumerable
。您尝试做的是直接在 DataContext
本身上绑定属性,这是行不通的。您必须至少有两个选项才能使其工作,这两个选项本质上都提供了单个项目的集合,如 ItemsSource
到嵌套的 DataGrid
.
创建一个值转换器,将您的
TableData
项目包装在一个集合中(这里是一个数组)。public class ItemToCollectionConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { return value is null ? null : new[] { value }; } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { throw new InvalidOperationException(); } }
在 XAML 中创建一个转换器实例(例如在您的
DataGrid.Resources
中)并简单地将使用转换器的数据上下文绑定到嵌套DataGrid
的ItemsSource
].通过这种方式,您的TableData
项目被包裹在一个集合中并被视为单个项目集合,因此列绑定将正常工作。<DataGrid x:Name="dataGrid" Margin="10,100,10,96" PreviewKeyDown="dataGrid_PreviewKeyDown" AutoGenerateColumns="False" ItemsSource="{Binding}" AlternatingRowBackground="AliceBlue" CellEditEnding="onCellEditEnding" SelectedItem="{Binding SelectedItem}" > <DataGrid.Resources> <local:ItemToCollectionConverter x:Key="ItemToCollectionConverter"/> </DataGrid.Resources> <DataGrid.Columns> <DataGridTextColumn Width="*" Header="No" Binding= "{Binding Path=Id, Mode=TwoWay}" IsReadOnly="True" /> <DataGridTextColumn Width="*" Header="Barcode" Binding= "{Binding Path=Barcode, Mode=TwoWay}" /> <DataGridTextColumn Width="*" Header="Item" Binding= "{Binding Path=Item, Mode=TwoWay}" IsReadOnly="True"/> <DataGridTextColumn Width="*" Header="Qty" Binding= "{Binding Path=Qty, Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/> <DataGridTextColumn Width="*" Header="Mrp" Binding= "{Binding Path=Mrp, Mode=TwoWay}" IsReadOnly="True"/> <DataGridTextColumn Width="*" Header="Discount" Binding= "{Binding Path=Discount, Mode=TwoWay}" IsReadOnly="True"/> <DataGridTextColumn Width="*" Header="Cgst" Binding= "{Binding Path=Cgst, Mode=TwoWay}" IsReadOnly="True"/> <DataGridTextColumn Width="*" Header="Sgst" Binding= "{Binding Path=Sgst, Mode=TwoWay}" IsReadOnly="True"/> <DataGridTextColumn Width="*" Header="Amount" Binding= "{Binding Path=Amount, Mode=OneWay ,UpdateSourceTrigger=PropertyChanged}" IsReadOnly="True"/> <DataGridTextColumn Width="*" Header="Salesman" Binding= "{Binding Path=Salesman, Mode=TwoWay}" IsReadOnly="True"/> <DataGridTemplateColumn Header="Delete"> <DataGridTemplateColumn.CellTemplate> <DataTemplate> <Button Content="X" Click="Button_Click"/> </DataTemplate> </DataGridTemplateColumn.CellTemplate> </DataGridTemplateColumn> </DataGrid.Columns> <DataGrid.RowDetailsTemplate> <DataTemplate> <DataGrid x:Name="RowDetailsDataGrid" AutoGenerateColumns="False" ItemsSource="{Binding Converter={StaticResource ItemToCollectionConverter}}"> <DataGrid.Columns> <DataGridTextColumn Width="*" Binding="{Binding Details}" /> <DataGridTextColumn Width="*" Binding="{Binding CgstDetails}" /> <DataGridTextColumn Width="*" Binding="{Binding SgstDetails}" /> <DataGridTextColumn Width="*" Binding="{Binding DiscountDetails}"/> </DataGrid.Columns> </DataGrid> </DataTemplate> </DataGrid.RowDetailsTemplate> </DataGrid>
从您的
TableData
类型公开一个集合 属性,其中包含子类型或仅包含详细信息属性的辅助类型的单个项目。public class TableDetailsData { public string Details { get; set; } public string CgstDetails { get; set; } public string SgstDetails { get; set; } public string DiscountDetails { get; set; } }
public class TableData : INotifyPropertyChanged { private TableDetailsData _detailsData; public TableDetailsData DetailsData { get => _detailsData; set { _detailsData = value; DetailsDataCollection = new List<TableDetailsData> { _detailsData }; OnPropertyChanged(nameof(DetailsDataCollection)); } } public IEnumerable<TableDetailsData> DetailsDataCollection { get; private set; } // ...other code. }
中将此集合绑定为DetailsDataCollection
始终在DetailsData
更改时更新。无论您是像这样创建集合并将DetailsData
公开为对象还是属性,都取决于您,有很多可能性,这只是为了了解要做什么。现在您可以在嵌套的DataGrid
.ItemsSource
<DataGrid x:Name="RowDetailsDataGrid" AutoGenerateColumns="False" ItemsSource="{Binding DetailsDataCollection}" CanUserAddRows="False">
就个人而言,我更喜欢值转换器解决方案,因为您不必公开冗余集合 属性 并污染您的代码,因为它不是一个非常干净的解决方案。我建议您创建一个适当的详细信息类型,并考虑一种比单行更好的方式来表示数据 DataGrid
,这似乎不合适并且在这种情况下不能提供良好的用户体验。