如何在 WPF 中对 DataGrid 列标题进行分组

问题描述 投票:0回答:4

是否可以在 WPF 数据网格中执行此操作:

|-------------- A header------------|---------------B Header---------------|

|-----A1Header----|----A2Header-----|-----B1Header-----|-----B2Header------|
|-----A1Data------|----A2 Data------|-----B1 Data------|-----B2 Data-------|
|-----A1Data------|----A2 Data------|-----B1 Data------|-----B2 Data-------|

谢谢。

wpf datagrid grouping
4个回答
10
投票

此主题可能会帮助您实现您想要做的事情。

它不直接从 DataGrid 获取功能,而是将 DataGrid 包装在常规网格中,并使用绑定列(具有多列跨度)来添加超级标题。

希望有一个很好的简单方法可以直接从 DataGrid 执行此操作,但如果没有,也许这对您来说是一个可以接受的解决方法。


4
投票

我可以提供三种解决方案来解决分组列的问题。

解决方案1

通过源集合的

ICollectionView
使用常规分组。这些组是垂直的,意味着它们共享相同的列。


解决方案2

创建嵌套数据源。这个想法是,每列绑定到一个单独的数据集,该数据集由添加到列的

DataGrid
DataGridTemplateColumn
显示。每个列组都有一个
DataGrid
。该方案的缺点是对数据结构的约束非常严格。不支持数据表,也不自动生成列。如果允许列排序或重新排序,工作量就会增加。但对于简单显示分组表来说,这个解决方案已经足够了。

使用示例

MainWindow.xaml

<Window>
  <Window.DataContext>
    <ViewModel />
  </Window.DataContext>

  <!-- Toplevel DataGrid that displays the column group headers -->
  <DataGrid ItemsSource="{Binding Rows}"
            AutoGenerateColumns="False"
            CanUserAddRows="False">

    <!-- The grouped column definitions -->
    <DataGrid.Columns>
      <DataGridTemplateColumn>
        <DataGridTemplateColumn.Header>
          <TextBlock Text="{Binding RelativeSource={RelativeSource AncestorType=DataGrid}, Path=Items[0].Columns[0].GroupHeader}"></TextBlock>
        </DataGridTemplateColumn.Header>

        <DataGridTemplateColumn.CellTemplate>
          <DataTemplate DataType="{x:Type local:DataGridRowItem}">
            <DataGrid ItemsSource="{Binding Columns[0].TableData}"
                      local:DataGridHelper.IsSynchronizeSelectedRowEnabled="True"
                      local:DataGridHelper.SynchronizeGroupKey="A"
                      RowHeaderWidth="0"
                      BorderThickness="0" />
          </DataTemplate>
        </DataGridTemplateColumn.CellTemplate>
        <DataGridTemplateColumn.CellEditingTemplate>
          <DataTemplate DataType="{x:Type local:DataGridRowItem}">
            <TextBox />
          </DataTemplate>
        </DataGridTemplateColumn.CellEditingTemplate>
      </DataGridTemplateColumn>
      <DataGridTemplateColumn>
        <DataGridTemplateColumn.Header>
          <TextBlock Text="{Binding RelativeSource={RelativeSource AncestorType=DataGrid}, Path=Items[0].Columns[1].GroupHeader}"></TextBlock>
        </DataGridTemplateColumn.Header>

        <DataGridTemplateColumn.CellTemplate>
          <DataTemplate DataType="{x:Type local:DataGridRowItem}">
            <DataGrid ItemsSource="{Binding Columns[1].TableData}"
                      local:DataGridHelper.IsSynchronizeSelectedRowEnabled="True"
                      local:DataGridHelper.SynchronizeGroupKey="A"
                      RowHeaderWidth="0"
                      BorderThickness="0" />
          </DataTemplate>
        </DataGridTemplateColumn.CellTemplate>
        <DataGridTemplateColumn.CellEditingTemplate>
          <DataTemplate DataType="{x:Type local:DataGridRowItem}">
            <TextBox />
          </DataTemplate>
        </DataGridTemplateColumn.CellEditingTemplate>
      </DataGridTemplateColumn>
    </DataGrid.Columns>
  </DataGrid>
</Window>

建议通过

DataGrid
禁用表修改。要美化外观,例如将组名称居中或覆盖
DataGridRow
模板来为未聚焦的网格添加行突出显示,非常简单。

实现示例

嵌套表的数据结构:

DataGridRowItem.cs
根元素。顶级的单行项目

DataGrid

这将显示列组标题。每个
DataGridColumnGroupItem
都有一个列组。

public class DataGridRowItem
{
  public List<DataGridColumnGroupItem> Columns { get; set; }
}

DataGridColumnGroupItem.cs
每个

DataGridColumnGroupItem
将构成一组列。

public class DataGridColumnGroupItem
{
  public string GroupHeader { get; set; }
  public List<Appointment> TableData { get; set; }
}

预约.cs
组中显示的实际数据模型

DataGrid
.

public class Appointment
{
  public DateTime Start { get; set; }
  public DateTime End { get; set; }
}

ViewModel.cs

public class TestViewModel : ViewModel
{
  public List<DataGridRowItem> Rows { get; }

  public ViewModel()
  {
    this.GroupingRow = new List<DataGridRowItem>
    {
      // The single row for the grouping top level DataGrid
      new DataGridRowItem()
      {  
        Columns = new List<DataGridColumnGroupItem>()
        {
          // First column group
          new DataGridColumnGroupItem()
          {
            GroupHeader = "Group 1",
            TableData = new List<Appointment>
            {
              new Appointment() { Start = DateTime.Now.AddDays(1), End = DateTime.Now.AddDays(2) },
              new Appointment() { Start = DateTime.Now.AddDays(5), End = DateTime.Now.AddDays(6) }
            }
          },

          // Second column group
          new DataGridColumnGroupItem()
          {
            GroupHeader = "Group 2",
            TableData = new List<Appointment>
            {
              new Appointment() { Start = DateTime.Now.AddDays(3), End = DateTime.Now.AddDays(4) },
              new Appointment() { Start = DateTime.Now.AddDays(7), End = DateTime.Now.AddDays(8) }
            }
          }
        }
      }
    };
  }
}

DataGridHelper.cs
一种附加行为,有助于跨多个

DataGrid
实例同步所选行。该行为最初是为不同的问题编写的,但也可以在这种情况下重用。它允许创建
DataGrid
元素的同步组。

public class DataGridHelper : DependencyObject
{
  public static object GetSynchronizeGroupKey(DependencyObject attachedElement)
    => (object)attachedElement.GetValue(SynchronizeGroupKeyProperty);
  public static void SetSynchronizeGroupKey(DependencyObject attachedElement, object value)
    => attachedElement.SetValue(SynchronizeGroupKeyProperty, value);

  public static readonly DependencyProperty SynchronizeGroupKeyProperty = DependencyProperty.RegisterAttached(
    "SynchronizeGroupKey",
    typeof(object),
    typeof(DataGridHelper),
    new PropertyMetadata(default(object), OnSynchronizeGroupKeyChanged));

  public static bool GetIsSynchronizeSelectedRowEnabled(DependencyObject attachedElement)
    => (bool)attachedElement.GetValue(IsSynchronizeSelectedRowEnabledProperty);
  public static void SetIsSynchronizeSelectedRowEnabled(DependencyObject attachedElement, bool value)
    => attachedElement.SetValue(IsSynchronizeSelectedRowEnabledProperty, value);

  public static readonly DependencyProperty IsSynchronizeSelectedRowEnabledProperty = DependencyProperty.RegisterAttached(
    "IsSynchronizeSelectedRowEnabled",
    typeof(bool),
    typeof(DataGridHelper),
    new PropertyMetadata(default(bool), OnIsSynchronizeSelectedRowEnabledChanged));

  private static Dictionary<object, IList<WeakReference<DataGrid>>> DataGridTable { get; } = new Dictionary<object, IList<WeakReference<DataGrid>>>();
  private static void OnIsSynchronizeSelectedRowEnabledChanged(DependencyObject attachingElement, DependencyPropertyChangedEventArgs e)
  {
    if (attachingElement is not DataGrid dataGrid)
    {
      throw new ArgumentException($"Attaching element must of type {typeof(DataGrid)}.", nameof(attachingElement));
    }

    if ((bool)e.NewValue)
    {
      RegisterDataGridForSelectedItemSynchronization(dataGrid);
    }
    else
    {
      UnregisterDataGridForSelectedItemSynchronization(dataGrid);
    }
  }

  private static void RegisterDataGridForSelectedItemSynchronization(DataGrid dataGrid)
    => WeakEventManager<DataGrid, SelectionChangedEventArgs>.AddHandler(dataGrid, nameof(DataGrid.SelectionChanged), SynchronizeSelectedItem_OnSelectionChanged);

  private static void UnregisterDataGridForSelectedItemSynchronization(DataGrid dataGrid)
    => WeakEventManager<DataGrid, SelectionChangedEventArgs>.RemoveHandler(dataGrid, nameof(DataGrid.SelectionChanged), SynchronizeSelectedItem_OnSelectionChanged);

  private static void OnSynchronizeGroupKeyChanged(DependencyObject attachingElement, DependencyPropertyChangedEventArgs e)
  {
    if (attachingElement is not DataGrid dataGrid)
    {
      throw new ArgumentException($"Attaching element must of type {typeof(DataGrid)}.", nameof(attachingElement));
    }

    if (e.NewValue == null)
    {
      throw new ArgumentNullException($"{null} is not a valid value for the attached property {nameof(SynchronizeGroupKeyProperty)}.", nameof(e.NewValue));
    }

    if (!DataGridTable.TryGetValue(e.NewValue, out IList<WeakReference<DataGrid>>? dataGridGroup))
    {
      dataGridGroup = new List<WeakReference<DataGrid>>();
      DataGridTable.Add(e.NewValue, dataGridGroup);
    }

    dataGridGroup.Add(new WeakReference<DataGrid>(dataGrid));
  }

  private static void SynchronizeSelectedItem_OnSelectionChanged(object sender, SelectionChangedEventArgs e)
  {
    var synchronizationSourceDataGrid = sender as DataGrid;
    var synchronizationSourceDataGridGroupKey = GetSynchronizeGroupKey(synchronizationSourceDataGrid);
    if (!DataGridTable.TryGetValue(synchronizationSourceDataGridGroupKey, out IList<WeakReference<DataGrid>> dataGridGroup))
    {
      return;
    }

    var selectedIndices = synchronizationSourceDataGrid.SelectedItems
      .Cast<object>()
      .Select(synchronizationSourceDataGrid.Items.IndexOf)
      .ToList();

    foreach (WeakReference<DataGrid> dataGridReference in dataGridGroup)
    {
      if (!dataGridReference.TryGetTarget(out DataGrid dataGrid)
        || dataGrid == synchronizationSourceDataGrid
        || dataGrid.Items.Count == 0)
      {
        continue;
      }

      UnregisterDataGridForSelectedItemSynchronization(dataGrid);
      dataGrid.SelectedItems.Clear();
      foreach (int selectedItemIndex in selectedIndices)
      {
        var selectedItem = dataGrid.Items[selectedItemIndex];
        dataGrid.SelectedItems.Add(selectedItem);
      }

      RegisterDataGridForSelectedItemSynchronization(dataGrid);
    }
  }
}

解决方案3

一个更强大的解决方案是实现自定义控件。这样,例如,重新排序/调整列大小、添加/删除行和自定义都非常方便。
自定义控件

GroupingDataGrid
基本上将自定义
DataGrid
包装到
Grid
中。
该解决方案支持自动生成以及显式列定义。可以调整列组和各个列的大小。当然,调整组标题的大小也会调整该组的列的大小。
DataGrid
托管的
GroupingDataGrid
,可以不受任何限制地使用:

<GroupingDataGrid>

  <!-- Define a DataGrid as usual -->
  <DataGrid ItemsSource="{Binding DataGridItems}" />
</GroupingDataGrid>

布局干净,列组的定义(使用

GroupDefinition
风格的
Grid.ColumnDefinitions
元素)非常方便。
要自定义组标题,请定义一个以
Style
为目标的
GroupingDataGridHeader
(这是一个
ContenControl
)。
GroupingDataGrid
是我库中的现有控件。我从源代码中删除了一些代码,主要是模板等自定义功能,以使帖子尽可能简洁。

使用示例

<local:GroupingDataGrid>
  <local:GroupingDataGrid.GroupDefinitions>

    <!-- Group from column 0 to 3 -->
    <local:GroupDefinition ColumnSpan="4"
                           Header="Person" />

    <!-- Second group from column 4 to 5 -->
    <local:GroupDefinition Column="4"
                           ColumnSpan="2"
                           Header="Numbers" />

    <!-- Remaining columns are automatically added 
         to a common unnamed group -->
  </local:GroupingDataGrid.GroupDefinitions>

    <!-- Define DataGrid as usual -->
    <DataGrid ItemsSource="{Binding DataGridItems}" />
</local:GroupingDataGrid>

源代码

分组数据网格.cs

[ContentProperty(nameof(GroupingDataGrid.DataGrid))]
public class GroupingDataGrid : Control
{
  public GroupDefinitionCollection GroupDefinitions
  {
    get => (GroupDefinitionCollection)GetValue(GroupDefinitionsProperty);
    set => SetValue(GroupDefinitionsProperty, value);
  }

  public static readonly DependencyProperty GroupDefinitionsProperty = DependencyProperty.Register(
    "GroupDefinitions",
    typeof(GroupDefinitionCollection),
    typeof(GroupingDataGrid),
    new PropertyMetadata(default));

  public DataGrid DataGrid
  {
    get { return (DataGrid)GetValue(DataGridProperty); }
    set { SetValue(DataGridProperty, value); }
  }

  public static readonly DependencyProperty DataGridProperty = DependencyProperty.Register(
    "DataGrid",
    typeof(DataGrid),
    typeof(GroupingDataGrid),
    new PropertyMetadata(default(DataGrid), OnDataGridChanged));

  static GroupingDataGrid()
  {
    DefaultStyleKeyProperty.OverrideMetadata(typeof(GroupingDataGrid), new FrameworkPropertyMetadata(typeof(GroupingDataGrid)));
  }
  private static void OnDataGridChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    => (d as GroupingDataGrid).OnDataGridChanged(e.OldValue as DataGrid, e.NewValue as DataGrid);

  private bool IsDataGridLayoutDirty { get; set; }
  private Grid GroupHost { get; }
  private Dictionary<Thumb, GroupingDataGridHeader> ThumbToGroupingDataGridHeaderTable { get; }
  private Dictionary<GroupDefinition, GroupingDataGridHeader> GroupDefinitionToGroupingDataGridHeaderTable { get; }

  public GroupingDataGrid()
  {
    this.GroupDefinitions = new GroupDefinitionCollection();
    this.ThumbToGroupingDataGridHeaderTable = new Dictionary<Thumb, GroupingDataGridHeader>();
    this.GroupDefinitionToGroupingDataGridHeaderTable = new Dictionary<GroupDefinition, GroupingDataGridHeader>();
    this.GroupHost = new Grid();
    this.GroupHost.RowDefinitions.Add(new RowDefinition() { Height = GridLength.Auto });
    this.GroupHost.RowDefinitions.Add(new RowDefinition() { Height = GridLength.Auto });
  }

  public override void OnApplyTemplate()
  {
    base.OnApplyTemplate();
    var contentHost = GetTemplateChild("PART_DataGridHost") as ContentPresenter;
    if (contentHost != null)
    {
      contentHost.Content = this.GroupHost;
    }
  }

  protected virtual void OnDataGridChanged(DataGrid oldDataGrid, DataGrid newDataGrid)
  {
    if (oldDataGrid != null)
    {
      this.GroupHost.Children.Remove(oldDataGrid);
      oldDataGrid.ColumnDisplayIndexChanged -= OnColumnOrderChanged;
      oldDataGrid.AutoGeneratedColumns -= OnDataGridAutoGeneratedColumns;
    }

    if (newDataGrid == null)
    {
      return;
    }

    this.IsDataGridLayoutDirty = true;
    this.GroupHost.Children.Add(this.DataGrid);
    newDataGrid.ColumnDisplayIndexChanged += OnColumnOrderChanged;
    if (newDataGrid.AutoGenerateColumns && !newDataGrid.IsLoaded)
    {
      newDataGrid.AutoGeneratedColumns += OnDataGridAutoGeneratedColumns;
    }
    else
    {
      CreateVisualTree();
    }
  }

  private void OnColumnOrderChanged(object? sender, DataGridColumnEventArgs e)
    => CreateVisualTree();

  private void OnDataGridAutoGeneratedColumns(object sender, EventArgs e)
    => CreateVisualTree();

  private void CreateVisualTree()
  {
    CreateGroups();
    if (this.IsDataGridLayoutDirty)
    {
      LayoutDataGrid();
    }
  }

  private void CreateGroups()
  {
    this.ThumbToGroupingDataGridHeaderTable.Clear();
    this.GroupDefinitionToGroupingDataGridHeaderTable.Clear();
    ClearGroupHost();

    AddRowHeaderColumnGroup();

    List<DataGridColumn> sortedColumns = this.DataGrid.Columns
    .OrderBy(column => column.DisplayIndex)
    .ToList();
    int ungroupedColumnCount = sortedColumns.Count - this.GroupDefinitions.Sum(definition => definition.ColumnSpan);
    bool hasUngroupedColumns = ungroupedColumnCount > 0;

    for (int groupIndex = 0; groupIndex < this.GroupDefinitions.Count; groupIndex++)
    {
      GroupDefinition group = this.GroupDefinitions[groupIndex];
      int groupHeaderColumnIndex = groupIndex + 1;

      AddGridColumn();
      AddGroupHeader(group, groupHeaderColumnIndex, sortedColumns);
      if (groupHeaderColumnIndex > 1)
      {
        GroupDefinition previousGroup = this.GroupDefinitions[groupIndex - 1];
        AddColumnGrippers(previousGroup, groupHeaderColumnIndex - 1);
      }
    }

    if (hasUngroupedColumns)
    {
      AddGroupForRemainingColumns();
    }
  }

  private void AddGroupForRemainingColumns()
  {
    AddGridColumn(false);
    AddGroupHeader(null, this.GroupHost.ColumnDefinitions.Count - 1, new List<DataGridColumn>());

    if (this.GroupDefinitions.Any())
    {
      GroupDefinition previousGroup = this.GroupDefinitions.Last();
      AddColumnGrippers(previousGroup, this.GroupDefinitions.Count);
    }
  }

  private void CreateColumnGroupHeaderBinding(IList<DataGridColumn> sortedColumns, GroupingDataGridHeader groupHeaderHost)
  {
    GroupDefinition group = groupHeaderHost.GroupDefinition;
    var groupHeaderWidthMultiBinding = new MultiBinding
    {
      Mode = BindingMode.TwoWay,
      Converter = new DataGridColumnRangeWidthToGroupHeaderWidthConverter(sortedColumns),
      ConverterParameter = group
    };

    for (int columnIndex = group.Column; columnIndex < group.Column + group.ColumnSpan; columnIndex++)
    {
      DataGridColumn column = sortedColumns[columnIndex];
      var widthBinding = new Binding(nameof(DataGridColumn.Width))
      {
        Mode = BindingMode.TwoWay,
        Source = column
      };
      groupHeaderWidthMultiBinding.Bindings.Add(widthBinding);
    }

    groupHeaderHost.SetBinding(WidthProperty, groupHeaderWidthMultiBinding);
  }

  private GroupingDataGridHeader AddGroupHeader(GroupDefinition group, int groupHeaderColumnIndex, List<DataGridColumn> sortedColumns)
  {
    var groupHeaderHost = new GroupingDataGridHeader(group);
    Grid.SetColumn(groupHeaderHost, groupHeaderColumnIndex);
    Grid.SetRow(groupHeaderHost, 0);
    this.GroupHost.Children.Add(groupHeaderHost);
     
    if (group != null)
    {
      this.GroupDefinitionToGroupingDataGridHeaderTable.Add(group, groupHeaderHost);
      if (sortedColumns.Any())
      {
        CreateColumnGroupHeaderBinding(sortedColumns, groupHeaderHost);
      }
    }

    return groupHeaderHost;
  }

  private void AddGridColumn(bool isAutoWidth = true)
  {
    var gridColumnWidth = isAutoWidth 
      ? GridLength.Auto 
      : new GridLength(1, GridUnitType.Star);
    var groupHeaderHostColumnDefinition = new ColumnDefinition() { Width = gridColumnWidth };
    this.GroupHost.ColumnDefinitions.Add(groupHeaderHostColumnDefinition);
  }

  private void AddColumnGrippers(GroupDefinition groupDefinition, int groupHeaderColumnIndex)
  {
    GroupingDataGridHeader groupHeaderHost = this.GroupDefinitionToGroupingDataGridHeaderTable[groupDefinition];
    AddColumnGripper(groupHeaderColumnIndex, groupHeaderHost, true);
    AddColumnGripper(groupHeaderColumnIndex + 1, groupHeaderHost);
  }

  private void AddColumnGripper(int columnIndex, GroupingDataGridHeader groupHeader, bool isLeftColumnGripper = false)
  {
    var columnGripper = new Thumb()
    {
      HorizontalAlignment = isLeftColumnGripper
        ? HorizontalAlignment.Right
        : HorizontalAlignment.Left,
    };

    columnGripper.DragDelta += OnGroupHeaderResizing;
    this.ThumbToGroupingDataGridHeaderTable.Add(columnGripper, groupHeader);
    Grid.SetColumn(columnGripper, columnIndex);
    Grid.SetRow(columnGripper, 0);
    this.GroupHost.Children.Add(columnGripper);
  }

  private void LayoutDataGrid()
  {
    Grid.SetColumnSpan(this.DataGrid, this.GroupHost.ColumnDefinitions.Count);
    Grid.SetRow(this.DataGrid, 1);
    this.IsDataGridLayoutDirty = false;
  }

  private void AddRowHeaderColumnGroup()
  {
    AddGridColumn();
    GroupingDataGridHeader rowHeaderGroupHost = AddGroupHeader(null, 0, new List<DataGridColumn>());
    var rowHeaderWidthBinding = new Binding(nameof(DataGrid.RowHeaderActualWidth))
    {
      Source = this.DataGrid
    };

    rowHeaderGroupHost.SetBinding(WidthProperty, rowHeaderWidthBinding);
  }

  private void ClearGroupHost()
  {
    for (int childIndex = this.GroupHost.Children.Count - 1; childIndex >= 0; childIndex--)
    {
      var child = this.GroupHost.Children[childIndex];
      if (child != this.DataGrid)
      {
        this.GroupHost.Children.Remove(child);
      }
    }
  }

  private void OnGroupHeaderResizing(object sender, DragDeltaEventArgs e)
  {
    var thumb = sender as Thumb;
    if (this.ThumbToGroupingDataGridHeaderTable.TryGetValue(thumb, out GroupingDataGridHeader groupingDataGridHeader))
    {
      groupingDataGridHeader.Width += e.HorizontalChange;
    }
  }
}

GroupingDataGridHeader.cs

public class GroupingDataGridHeader : ContentControl
{
  public GroupDefinition GroupDefinition { get; }

  public GroupingDataGridHeader() : this(new GroupDefinition())
  {      
  }

  public GroupingDataGridHeader(GroupDefinition groupDefinition)
  {
    this.GroupDefinition = groupDefinition;
    this.Content = this.GroupDefinition?.Header ?? string.Empty;
  }

  static GroupingDataGridHeader()
  {
    DefaultStyleKeyProperty.OverrideMetadata(typeof(GroupingDataGridHeader), new FrameworkPropertyMetadata(typeof(GroupingDataGridHeader)));
  }
}

GroupDefinition.cs

public class GroupDefinition : FrameworkContentElement
{
  public int Column
  {
    get => (int)GetValue(ColumnProperty);
    set => SetValue(ColumnProperty, value);
  }

  public static readonly DependencyProperty ColumnProperty = DependencyProperty.Register(
    "Column",
    typeof(int),
    typeof(GroupDefinition),
    new PropertyMetadata(default));

  public int ColumnSpan
  {
    get => (int)GetValue(ColumnSpanProperty);
    set => SetValue(ColumnSpanProperty, value);
  }

  public static readonly DependencyProperty ColumnSpanProperty = DependencyProperty.Register(
    "ColumnSpan",
    typeof(int),
    typeof(GroupDefinition),
    new PropertyMetadata(default));

  public object Header
  {
    get => (object)GetValue(HeaderProperty);
    set => SetValue(HeaderProperty, value);
  }

  public static readonly DependencyProperty HeaderProperty = DependencyProperty.Register(
    "Header",
    typeof(object),
    typeof(GroupDefinition),
    new PropertyMetadata(default));
}

GroupDefinitionCollection.cs

public class GroupDefinitionCollection : Collection<GroupDefinition>
{ }

DataGridColumnRangeWidthToGroupHeaderWidthConverter.cs

public class DataGridColumnRangeWidthToGroupHeaderWidthConverter : IMultiValueConverter
{
  private IList<DataGridColumn> DataGridColumns { get; }

  public DataGridColumnRangeWidthToGroupHeaderWidthConverter(IList<DataGridColumn> dataGridColumns)
  {
    this.DataGridColumns = dataGridColumns;
  }

  public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
    => values.Cast<DataGridLength>().Sum(gridLength => gridLength.DisplayValue);

  public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
  {
    var groupDefinition = (GroupDefinition)parameter;
    double currentGroupedColumnsWidth = this.DataGridColumns
      .Skip(groupDefinition.Column)
      .Take(groupDefinition.ColumnSpan)
      .Select(column => column.Width.DisplayValue)
      .Sum();

    var result = new object[groupDefinition.ColumnSpan];
    Array.Fill(result, Binding.DoNothing);
    DataGridColumn lastGroupColumn = this.DataGridColumns[groupDefinition.Column + groupDefinition.ColumnSpan - 1];
    var newColumnWidth = new DataGridLength(lastGroupColumn.Width.DisplayValue + (double)value - currentGroupedColumnsWidth, DataGridLengthUnitType.Pixel);
    result[result.Length - 1] = newColumnWidth;

    return result;
  }
}

Generic.xaml

<ResourceDictionary>
  <Style TargetType="local:GroupingDataGrid">
    <Style.Resources>
      <Style TargetType="Thumb">
        <Setter Property="Width"
                Value="8" />
        <Setter Property="Background"
                Value="Transparent" />
        <Setter Property="Cursor"
                Value="SizeWE" />
        <Setter Property="BorderBrush"
                Value="Transparent" />
        <Setter Property="Template">
          <Setter.Value>
            <ControlTemplate TargetType="{x:Type Thumb}">
              <Border Background="{TemplateBinding Background}"
                      Padding="{TemplateBinding Padding}" />
            </ControlTemplate>
          </Setter.Value>
        </Setter>
      </Style>
    </Style.Resources>

    <Setter Property="BorderThickness"
            Value="0,0,1,0" />
    <Setter Property="BorderBrush"
            Value="Black" />
    <Setter Property="Template">
      <Setter.Value>
        <ControlTemplate TargetType="local:GroupingDataGrid">
          <Border BorderThickness="{TemplateBinding BorderThickness}"
                  BorderBrush="{TemplateBinding BorderBrush}"
                  Background="{TemplateBinding Background}"
                  Padding="{TemplateBinding Padding}">
            <ContentPresenter x:Name="PART_DataGridHost" />
          </Border>
        </ControlTemplate>
      </Setter.Value>
    </Setter>
  </Style>

  <Style TargetType="local:GroupingDataGridHeader">
    <Setter Property="BorderThickness"
            Value="0,0,1,0" />
    <Setter Property="BorderBrush"
            Value="Black" />
    <Setter Property="HorizontalContentAlignment"
            Value="Center" />
    <Setter Property="Template">
      <Setter.Value>
        <ControlTemplate TargetType="local:GroupingDataGridHeader">
          <Border BorderThickness="{TemplateBinding BorderThickness}"
                  BorderBrush="{TemplateBinding BorderBrush}"
                  Background="{TemplateBinding Background}"
                  Padding="{TemplateBinding Padding}">
            <ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
                              VerticalAlignment="{TemplateBinding VerticalContentAlignment}" />
          </Border>
        </ControlTemplate>
      </Setter.Value>
    </Setter>
  </Style>
</ResourceDictionary>

0
投票

如果可以选择付费,Telerik radGridView 有 列组


0
投票

我复制了解决方案3的确切源代码,并运行它,但是除了数据网格的标题之外没有标题,我使用的是net框架,我必须注释掉DataGridColumnRangeWidthToGroupHeaderWidthConverter.cs中的行Array.Fill ,有什么建议吗?

© www.soinside.com 2019 - 2024. All rights reserved.