简体   繁体   English

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

[英]How to group DataGrid Column Headers in WPF

Is it possible to do this in a WPF datagrid:是否可以在 WPF 数据网格中执行此操作:

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

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

Thanks.谢谢。

This Thread might help you achieve what you're trying to do. 此线程可能会帮助您实现您想要做的事情。

It doesn't get the functionality directly from the DataGrid, but instead the DataGrid is wrapped in a regular Grid, and uses bound columns (with multi-columnspan) to add super headers.它不直接从 DataGrid 获得功能,而是将 DataGrid 包装在常规 Grid 中,并使用绑定列(具有多列跨度)来添加超级标题。

Hopefully there's a nice easy way to do this directly from the DataGrid, but if not, maybe this will be an acceptable workaround for you.希望有一个很好的简单方法可以直接从 DataGrid 执行此操作,但如果没有,也许这对您来说是一个可以接受的解决方法。

I can offer thee solutions to the problem of grouping columns.我可以为您提供分组列问题的解决方案。

Solution 1解决方案 1

Using conventional grouping via the ICollectionView of the source collection.通过源集合的ICollectionView使用常规分组。 This groups are vertical, means they share the same columns.这些组是垂直的,意味着它们共享相同的列。


Solution 2解决方案 2

Create a nested data source.创建嵌套数据源。 The idea is that each column binds to an individual data set that is displayed by a DataGrid that is added to the column's DataGridTemplateColumn .这个想法是每列绑定到一个单独的数据集,该数据集由添加到列的DataGridTemplateColumnDataGrid显示。 It's a DataGrid for each column group.它是每个列组的DataGrid The disadvantage of this solution is that the constraints for the data structure are very strict.这种方案的缺点是对数据结构的约束非常严格。 No DataTable supported and no auto generation of columns.不支持 DataTable 并且不自动生成列。 The effort increases if column sorting or reordering is allowed.如果允许列排序或重新排序,则工作量会增加。 but for simple displaying of a grouped table, this solution is fine enough.但是对于分组表的简单显示,这个解决方案就足够了。

Usage example使用示例

在此处输入图像描述

MainWindow.xaml主窗口.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>

It's recommended to disable table modifications via the DataGrid .建议通过DataGrid禁用表修改。 To prettify the look like centering the group names or overriding the DataGridRow template to add row highlight for the unfocused grids is quite simple.要美化外观,例如将组名称居中或覆盖DataGridRow模板以为未聚焦的网格添加行突出显示,这非常简单。

Implementation example实现示例

The data structure for the nested tables:嵌套表的数据结构:

DataGridRowItem.cs DataGridRowItem.cs
The root element.根元素。 A single row item for the top-level DataGrid顶级DataGrid的单行项
that will display the column group headers.这将显示列组标题。 A column group for each DataGridColumnItem .每个DataGridColumnItem的列组。

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

DataGridColumnItem.cs DataGridColumnItem.cs
Each DataGridColumnItem will make a column group.每个DataGridColumnItem将组成一个列组。

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

Appointment.cs约会.cs
The actual data model that is displayed in the group's DataGrid .组的DataGrid中显示的实际数据 model 。

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

ViewModel.cs视图模型.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<DataGridColumnItem>()
        {
          // First column group
          new DataGridColumnItem()
          {
            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 DataGridColumnItem()
          {
            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数据网格助手.cs
An attached behavior that helps to synchronize the selected row across multiple DataGrid instances.一种附加行为,有助于跨多个DataGrid实例同步所选行。 the behavior was originally written for different problems, but can be reused in this scenario too.该行为最初是为不同的问题编写的,但也可以在这种情况下重用。 It allows to create synchronization groups of DataGrid elements.它允许创建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);
    }
  }
}

Solution 3解决方案 3

A more powerful solution is to implement a custom control.更强大的解决方案是实现自定义控件。 This way eg, reorder/resize columns, add/remove rows and customization are really convenient.这样,例如重新排序/调整列大小、添加/删除行和自定义非常方便。
The custom control GroupingDataGrid basically wraps a custom DataGrid into a Grid .自定义控件GroupingDataGrid基本上将自定义DataGrid包装到Grid中。
This solution supports auto generation as well as explicit column definitions.该解决方案支持自动生成以及显式列定义。 The column groups and the individual columns can be resized.可以调整列组和各个列的大小。
The DataGrid , that is hosted by the GroupingDataGrid , can be used without any restrictions.GroupingDataGrid托管的DataGrid可以不受任何限制地使用。 The layout is clean and the definition of column groups (using the GroupDefinition in the style of Grid definitions) is quite convenient.布局干净,列组的定义(使用Grid定义风格的GroupDefinition )非常方便。
To customize the group headers, define a Style that targets GroupingDataGridHeader (which is a ContenControl ).要自定义组标题,请定义一个以GroupingDataGridHeader为目标的Style (它是一个ContenControl )。
The GroupingDataGrid is an existing control from my library. GroupingDataGrid是我的库中的现有控件。 I removed some code, mainly customization features like templating, from the sources to keep the post as concise as possible.我从源代码中删除了一些代码,主要是模板等自定义功能,以使帖子尽可能简洁。

Usage example使用示例

![在此处输入图片说明

<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>

Source code源代码

GroupingDataGrid.cs分组数据网格.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;
    }
  }
}

GroupDefinition.cs组定义.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 GroupDefinitionCollection.cs

public class GroupDefinitionCollection : Collection<GroupDefinition>
{ }

DataGridColumnRangeWidthToGroupHeaderWidthConverter.cs 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通用.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>

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM