【问题标题】:How to display DataGrid inside DataGrid RowDetailsTemplate?如何在 DataGrid RowDetailsTemplate 中显示 DataGrid?
【发布时间】:2021-10-24 08:18:21
【问题描述】:

我想在另一个 DataGrids 行详细信息模板中添加一行 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);
}

这不起作用。而不是DataGrid,我只是在WrapPanel 内放了一个TextBlock(参见下面的代码),数据绑定工作完美,数据显示在行详细信息中,但我希望它是一行(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>

如果有人可以帮忙?

【问题讨论】:

    标签: c# wpf xaml data-binding datagrid


    【解决方案1】:

    我不认为将嵌套的DataGrid 放入行详细信息模板中只是为了显示单行是一个好主意,但如果这是您唯一的选择,您可以按以下方式进行。

    DataGrid 需要集合类型的 ItemsSource,更具体地说是 IEnumerable。您尝试做的是直接在DataContext 本身上绑定属性,这是行不通的。您必须至少有两个选项才能使其工作,这两个选项本质上都为嵌套的 DataGrid 提供了单个项目的集合,如 ItemsSource

    • 创建一个值转换器,将您的 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 更好的方式来表示数据,这似乎不合适,并且在这种情况下不能提供良好的用户体验。

    【讨论】:

      猜你喜欢
      • 2013-12-22
      • 1970-01-01
      • 2010-11-02
      • 2011-01-12
      • 1970-01-01
      • 1970-01-01
      • 2014-06-24
      • 2013-01-12
      • 1970-01-01
      相关资源
      最近更新 更多