如何在 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 中)并简单地将使用转换器的数据上下文绑定到嵌套 DataGridItemsSource ].通过这种方式,您的 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,这似乎不合适并且在这种情况下不能提供良好的用户体验。