使用DataGrid展示单元格合并数据

1. 引言

        本文介绍如何使用DataGrid进行合并单元格数据。

2. DataGrid 概述

        DataGrid 是一种用于在应用程序中显示和操作数据表格的控件。它常用于显示来自数据库或集合的数据,以便用户可以查看、编辑、排序和筛选这些数据。DataGrid 提供了一种灵活的方式来呈现和管理数据,使得开发人员可以快速构建用户界面。

3. 合并单元格的基本概念

        合并单元格是指将相邻的单元合并为一个大的单元格,这样可以使得表格数据呈现出更清晰的结构和逻辑关系。

4. 实现合并单元格的方法

  • 4.1.  定义数据结构

  • /// <summary>
    /// 数据类
    /// </summary>
    public class EquipmentInfo
    {
        /// <summary>
        /// 部门名称
        /// </summary>
        public string DepartmentName { get; set; }
        /// <summary>
        /// 设备名称
        /// </summary>
        public string EquipmentName { get; set; }
        /// <summary>
        /// 设备类型
        /// </summary>
        public string EquipmentType { get; set; }
        /// <summary>
        /// 设备状态
        /// </summary>
        public string EquipmentStatus { get; set; }
        public bool Equals(EquipmentInfo? other)
        {
            if (other == null) return false;
            return this.DepartmentName == other.DepartmentName && this.EquipmentName == other.EquipmentName && this.EquipmentType == other.EquipmentType;
        }
    }
  • 4.2. 定义计算单元格高度转化类

  •     /// <summary>
        /// 单元格高度转换类
        /// </summary>
        public class DataGridCellHeightConverter : IValueConverter
        {
            public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
            {
                int height = 40;
                int finalHeight = 40;
                if (value is null) return height;
                if (value is CollectionViewGroup viewGroup)
                {
                    var itemsCount = viewGroup.Items.Count;
                    finalHeight = height * itemsCount;
                }
                return finalHeight;
            }
    
            public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
            {
                return DependencyProperty.UnsetValue;
            }
        }
  • 4.3. 定义单元格数据源转换类

  • /// <summary>
    /// 单元格数据源转换类
    /// </summary>
    public class DataGridCellDataSourceConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if (value is null || parameter is null) return Enumerable.Empty<object>();
            var readOnlyCollection = value as ReadOnlyObservableCollection<object>;
            if (readOnlyCollection == null) return Enumerable.Empty<object>();
            if (int.TryParse(parameter.ToString(), out int displayIndex))
            {
                ObservableCollection<EquipmentInfo> collection = new();
                foreach (var item in readOnlyCollection)
                {
                    collection.Add((EquipmentInfo)item);
                }
                var view = CollectionViewSource.GetDefaultView(collection);
                if (displayIndex == 1)
                {
                    view.GroupDescriptions.Clear();
                    view.GroupDescriptions.Add(new PropertyGroupDescription("EquipmentType"));
                    return view.Groups;
                }
                if (displayIndex == 2)
                {
                    view.GroupDescriptions.Clear();
                    view.GroupDescriptions.Add(new PropertyGroupDescription("EquipmentName"));
                    return view.Groups;
                }
                if (displayIndex == 3)
                {
                    view.GroupDescriptions.Clear();
                    view.GroupDescriptions.Add(new PropertyGroupDescription("EquipmentStatus"));
                    return collection;
                }
                return collection;
            }
            return Enumerable.Empty<object>();
        }
    
        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            return DependencyProperty.UnsetValue;
        }
    }

    4.4 定义相关资源样式

  •   <Window.Resources>
          <SolidColorBrush x:Key="DataGrid.Background" Color="LightGray" />
          <Style x:Key="DataGrid.Base" TargetType="{x:Type DataGrid}">
              <Setter Property="AutoGenerateColumns" Value="False" />
              <Setter Property="RowHeaderWidth" Value="0" />
              <Setter Property="CanUserAddRows" Value="False" />
              <Setter Property="CanUserDeleteRows" Value="False" />
              <Setter Property="CanUserResizeColumns" Value="False" />
              <Setter Property="VirtualizingPanel.IsVirtualizing" Value="True" />
              <Setter Property="VirtualizingPanel.VirtualizationMode" Value="Recycling" />
              <Setter Property="VirtualizingPanel.CacheLengthUnit" Value="Item" />
              <Setter Property="VirtualizingPanel.ScrollUnit" Value="Item" />
              <Setter Property="ScrollViewer.CanContentScroll" Value="True" />
              <Setter Property="DataGrid.GridLinesVisibility" Value="Horizontal" />
              <Setter Property="DataGrid.HeadersVisibility" Value="Column" />
    
              <Setter Property="DataGrid.RowDetailsVisibilityMode" Value="VisibleWhenSelected" />
              <Setter Property="ScrollViewer.CanContentScroll" Value="true" />
              <Setter Property="ScrollViewer.PanningMode" Value="Both" />
              <Setter Property="Stylus.IsFlicksEnabled" Value="False" />
              <Setter Property="Template">
                  <Setter.Value>
                      <ControlTemplate TargetType="{x:Type DataGrid}">
                          <Border
                              Padding="{TemplateBinding Padding}"
                              Background="{TemplateBinding Background}"
                              BorderBrush="{TemplateBinding BorderBrush}"
                              BorderThickness="{TemplateBinding BorderThickness}"
                              SnapsToDevicePixels="True">
                              <ScrollViewer Name="DG_ScrollViewer" Focusable="false">
                                  <ScrollViewer.Template>
                                      <ControlTemplate TargetType="{x:Type ScrollViewer}">
                                          <Grid>
                                              <Grid.ColumnDefinitions>
                                                  <ColumnDefinition Width="Auto" />
                                                  <ColumnDefinition Width="*" />
                                                  <ColumnDefinition Width="Auto" />
                                              </Grid.ColumnDefinitions>
                                              <Grid.RowDefinitions>
                                                  <RowDefinition Height="Auto" />
                                                  <RowDefinition Height="*" />
                                                  <RowDefinition Height="Auto" />
                                              </Grid.RowDefinitions>
                                              <Button
                                                  Width="{Binding CellsPanelHorizontalOffset, RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}"
                                                  Command="{x:Static DataGrid.SelectAllCommand}"
                                                  Focusable="false"
                                                  Style="{DynamicResource {ComponentResourceKey ResourceId=DataGridSelectAllButtonStyle,
                                                                                                TypeInTargetAssembly={x:Type DataGrid}}}"
                                                  Visibility="{Binding HeadersVisibility, ConverterParameter={x:Static DataGridHeadersVisibility.All}, Converter={x:Static DataGrid.HeadersVisibilityConverter}, RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}" />
                                              <Border
                                                  Grid.Row="0"
                                                  Grid.Column="1"
                                                  Visibility="{Binding HeadersVisibility, ConverterParameter={x:Static DataGridHeadersVisibility.Column}, Converter={x:Static DataGrid.HeadersVisibilityConverter}, RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}">
                                                  <DataGridColumnHeadersPresenter Name="PART_ColumnHeadersPresenter" />
                                              </Border>
                                              <ScrollContentPresenter
                                                  Name="PART_ScrollContentPresenter"
                                                  Grid.Row="1"
                                                  Grid.Column="0"
                                                  Grid.ColumnSpan="2"
                                                  CanContentScroll="{TemplateBinding CanContentScroll}" />
                                              <ScrollBar
                                                  Name="PART_VerticalScrollBar"
                                                  Grid.Row="1"
                                                  Grid.Column="2"
                                                  Maximum="{TemplateBinding ScrollableHeight}"
                                                  Orientation="Vertical"
                                                  ViewportSize="{TemplateBinding ViewportHeight}"
                                                  Visibility="{TemplateBinding ComputedVerticalScrollBarVisibility}"
                                                  Value="{Binding VerticalOffset, Mode=OneWay, RelativeSource={RelativeSource TemplatedParent}}" />
                                              <Grid Grid.Row="2" Grid.Column="1">
                                                  <Grid.ColumnDefinitions>
                                                      <ColumnDefinition Width="{Binding NonFrozenColumnsViewportHorizontalOffset, RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}" />
                                                      <ColumnDefinition Width="*" />
                                                  </Grid.ColumnDefinitions>
                                                  <ScrollBar
                                                      Name="PART_HorizontalScrollBar"
                                                      Grid.Column="1"
                                                      Maximum="{TemplateBinding ScrollableWidth}"
                                                      Orientation="Horizontal"
                                                      ViewportSize="{TemplateBinding ViewportWidth}"
                                                      Visibility="{TemplateBinding ComputedHorizontalScrollBarVisibility}"
                                                      Value="{Binding HorizontalOffset, Mode=OneWay, RelativeSource={RelativeSource TemplatedParent}}" />
                                              </Grid>
                                          </Grid>
                                      </ControlTemplate>
                                  </ScrollViewer.Template>
                                  <ItemsPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
                              </ScrollViewer>
                          </Border>
                      </ControlTemplate>
                  </Setter.Value>
              </Setter>
              <Setter Property="DataGrid.VerticalGridLinesBrush" Value="{Binding HorizontalGridLinesBrush, RelativeSource={RelativeSource Self}}" />
              <Style.Triggers>
                  <MultiTrigger>
                      <MultiTrigger.Conditions>
                          <Condition Property="ItemsControl.IsGrouping" Value="true" />
                          <Condition Property="VirtualizingPanel.IsVirtualizingWhenGrouping" Value="false" />
                      </MultiTrigger.Conditions>
                      <Setter Property="ScrollViewer.CanContentScroll" Value="false" />
                  </MultiTrigger>
              </Style.Triggers>
          </Style>
          <!--  外部DataGrid  -->
          <Style
              x:Key="DataGrid.Main"
              BasedOn="{StaticResource DataGrid.Base}"
              TargetType="{x:Type DataGrid}">
              <Setter Property="Padding" Value="0" />
              <Setter Property="BorderBrush" Value="Black" />
              <Setter Property="BorderThickness" Value="2,2,1,2" />
              <Setter Property="HorizontalGridLinesBrush" Value="Transparent" />
              <Setter Property="VerticalGridLinesBrush" Value="Black" />
              <Setter Property="GridLinesVisibility" Value="All" />
              <Setter Property="Background" Value="{StaticResource DataGrid.Background}" />
          </Style>
          <!--  列标题样式  -->
          <Style x:Key="DataGridColumnHeaderStyle" TargetType="{x:Type DataGridColumnHeader}">
              <Setter Property="Background" Value="#1D90CE" />
              <Setter Property="HorizontalContentAlignment" Value="Center" />
              <Setter Property="Foreground" Value="White" />
              <Setter Property="FontSize" Value="15" />
              <Setter Property="Height" Value="30" />
              <Setter Property="Template">
                  <Setter.Value>
                      <ControlTemplate TargetType="{x:Type DataGridColumnHeader}">
                          <Border
                              Height="{TemplateBinding Height}"
                              Background="{TemplateBinding Background}"
                              BorderBrush="{Binding VerticalGridLinesBrush, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=DataGrid}}"
                              BorderThickness="0,0,1,1">
                              <ContentPresenter
                                  HorizontalAlignment="Center"
                                  VerticalAlignment="Center"
                                  Content="{TemplateBinding Content}" />
                          </Border>
                      </ControlTemplate>
                  </Setter.Value>
    
              </Setter>
          </Style>
          <!--  外部单元格样式  -->
          <Style x:Key="DataGridCell.Main" TargetType="{x:Type DataGridCell}">
              <Setter Property="Margin" Value="0" />
              <Setter Property="Padding" Value="0" />
              <Setter Property="BorderThickness" Value="0" />
              <Setter Property="Template">
                  <Setter.Value>
                      <ControlTemplate TargetType="{x:Type DataGridCell}">
                          <Border Background="{Binding Background, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=DataGrid}}">
                              <ContentPresenter />
                          </Border>
                      </ControlTemplate>
                  </Setter.Value>
              </Setter>
          </Style>
    
          <!--  内部单元格样式  -->
          <Style x:Key="DataGridCell.Inner" TargetType="{x:Type DataGridCell}">
              <Setter Property="Margin" Value="0" />
              <Setter Property="Padding" Value="0" />
              <Setter Property="BorderThickness" Value="0" />
              <Setter Property="VerticalContentAlignment" Value="Center" />
              <Setter Property="Template">
                  <Setter.Value>
                      <ControlTemplate TargetType="{x:Type DataGridCell}">
                          <Border
                              Background="{Binding Background, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=DataGrid}}"
                              BorderBrush="Black"
                              BorderThickness="0,1,0,0">
                              <ContentPresenter />
                          </Border>
                      </ControlTemplate>
                  </Setter.Value>
              </Setter>
          </Style>
          <!--  内部DataGrid样式  -->
          <Style
              x:Key="DataGrid.Inner"
              BasedOn="{StaticResource DataGrid.Base}"
              TargetType="{x:Type DataGrid}">
              <Setter Property="Margin" Value="0" />
              <Setter Property="Padding" Value="0" />
              <Setter Property="BorderThickness" Value="0" />
              <Setter Property="HeadersVisibility" Value="None" />
              <Setter Property="HorizontalGridLinesBrush" Value="Transparent" />
              <Setter Property="VerticalGridLinesBrush" Value="Transparent" />
              <Setter Property="Background" Value="{StaticResource DataGrid.Background}" />
          </Style>
          <!--  DataGridRow样式  -->
          <Style TargetType="{x:Type DataGridRow}">
              <Setter Property="Background" Value="Transparent" />
          </Style>
          <!--  TextBlcok样式  -->
          <Style x:Key="TextBlock.Style" TargetType="{x:Type TextBlock}">
              <Setter Property="HorizontalAlignment" Value="Center" />
              <Setter Property="VerticalAlignment" Value="Center" />
              <Setter Property="FontSize" Value="15" />
              <Setter Property="VerticalAlignment" Value="Bottom" />
              <Setter Property="TextOptions.TextFormattingMode" Value="Ideal" />
              <Setter Property="TextOptions.TextRenderingMode" Value="Auto" />
              <Setter Property="TextOptions.TextHintingMode" Value="Auto" />
              <Setter Property="UseLayoutRounding" Value="True" />
              <Setter Property="TextTrimming" Value="CharacterEllipsis" />
              <Setter Property="ToolTip" Value="{Binding Text, RelativeSource={RelativeSource Mode=Self}}" />
              <Setter Property="VerticalAlignment" Value="Center" />
              <Setter Property="FontFamily" Value="Microsoft YaHei" />
          </Style>
          <local:DataGridCellDataSourceConverter x:Key="DataGridCellDataSourceConverter" />
          <local:DataGridCellHeightConverter x:Key="DataGridCellHeightConverter" />
      </Window.Resources>

    4.5 定义DataGrid

  <Grid Margin="10">
      <Grid.RowDefinitions>
          <RowDefinition Height="auto" />
          <RowDefinition Height="*" />
      </Grid.RowDefinitions>
      <StackPanel Orientation="Horizontal">
          <Button
              x:Name="btnLoadData"
              Click="btnLoadData_Click"
              Content="加载数据" />
      </StackPanel>
      <DataGrid
          x:Name="datagrid"
          Grid.Row="1"
          Width="680"
          Margin="0,10,0,0"
          VerticalAlignment="Center"
          CellStyle="{StaticResource DataGridCell.Main}"
          Style="{StaticResource DataGrid.Main}">
          <DataGrid.Columns>
              <DataGridTemplateColumn
                  Width="197"
                  Header="部门名称"
                  HeaderStyle="{StaticResource DataGridColumnHeaderStyle}">
                  <DataGridTemplateColumn.CellTemplate>
                      <DataTemplate>
                          <Border
                              Width="196"
                              BorderBrush="{Binding VerticalGridLinesBrush, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=DataGrid}}"
                              BorderThickness="0,1,0,0">
                              <TextBlock Style="{StaticResource TextBlock.Style}" Text="{Binding Name}" />
                          </Border>
                      </DataTemplate>
                  </DataGridTemplateColumn.CellTemplate>
              </DataGridTemplateColumn>

              <DataGridTemplateColumn
                  Width="200"
                  Header="设备类型"
                  HeaderStyle="{StaticResource DataGridColumnHeaderStyle}">
                  <DataGridTemplateColumn.CellTemplate>
                      <DataTemplate>
                          <DataGrid
                              CellStyle="{StaticResource DataGridCell.Inner}"
                              ItemsSource="{Binding Items, Converter={StaticResource DataGridCellDataSourceConverter}, ConverterParameter=1}"
                              Style="{StaticResource DataGrid.Inner}">
                              <DataGrid.Columns>
                                  <DataGridTemplateColumn Width="*">
                                      <DataGridTemplateColumn.CellTemplate>
                                          <DataTemplate>
                                              <Grid Height="{Binding ., Converter={StaticResource DataGridCellHeightConverter}}">
                                                  <TextBlock Style="{StaticResource TextBlock.Style}" Text="{Binding Name}" />
                                              </Grid>
                                          </DataTemplate>
                                      </DataGridTemplateColumn.CellTemplate>
                                  </DataGridTemplateColumn>
                              </DataGrid.Columns>
                          </DataGrid>
                      </DataTemplate>
                  </DataGridTemplateColumn.CellTemplate>
              </DataGridTemplateColumn>
              <DataGridTemplateColumn
                  Width="200"
                  Header="设备名称"
                  HeaderStyle="{StaticResource DataGridColumnHeaderStyle}">
                  <DataGridTemplateColumn.CellTemplate>
                      <DataTemplate>
                          <DataGrid
                              Margin="0"
                              CanUserAddRows="False"
                              CanUserDeleteRows="False"
                              CellStyle="{StaticResource DataGridCell.Inner}"
                              ItemsSource="{Binding Items}"
                              RowHeight="40"
                              Style="{StaticResource DataGrid.Inner}">
                              <DataGrid.Columns>
                                  <DataGridTemplateColumn Width="*">
                                      <DataGridTemplateColumn.CellTemplate>
                                          <DataTemplate>
                                              <TextBlock Style="{StaticResource TextBlock.Style}" Text="{Binding EquipmentName}" />
                                          </DataTemplate>
                                      </DataGridTemplateColumn.CellTemplate>
                                  </DataGridTemplateColumn>
                              </DataGrid.Columns>
                          </DataGrid>
                      </DataTemplate>
                  </DataGridTemplateColumn.CellTemplate>
              </DataGridTemplateColumn>
              <DataGridTemplateColumn
                  Width="80"
                  Header="状态"
                  HeaderStyle="{StaticResource DataGridColumnHeaderStyle}">
                  <DataGridTemplateColumn.CellTemplate>
                      <DataTemplate>
                          <DataGrid
                              Margin="0"
                              CellStyle="{StaticResource DataGridCell.Inner}"
                              ItemsSource="{Binding Items, Converter={StaticResource DataGridCellDataSourceConverter}, ConverterParameter=3}"
                              MinRowHeight="40"
                              Style="{StaticResource DataGrid.Inner}">
                              <DataGrid.GroupStyle>
                                  <GroupStyle>
                                      <GroupStyle.ContainerStyle>
                                          <Style TargetType="{x:Type GroupItem}">
                                              <Setter Property="VerticalContentAlignment" Value="Center" />
                                              <Setter Property="Template">
                                                  <Setter.Value>
                                                      <ControlTemplate TargetType="{x:Type GroupItem}">
                                                          <Border
                                                              Height="{Binding ., Converter={StaticResource DataGridCellHeightConverter}}"
                                                              VerticalAlignment="Center"
                                                              BorderBrush="Black"
                                                              BorderThickness="0,1,0,0">
                                                              <TextBlock
                                                                  HorizontalAlignment="Center"
                                                                  VerticalAlignment="Center"
                                                                  Text="{Binding Name}" />
                                                          </Border>
                                                      </ControlTemplate>
                                                  </Setter.Value>
                                              </Setter>
                                          </Style>
                                      </GroupStyle.ContainerStyle>
                                  </GroupStyle>
                              </DataGrid.GroupStyle>
                          </DataGrid>
                      </DataTemplate>
                  </DataGridTemplateColumn.CellTemplate>
              </DataGridTemplateColumn>
          </DataGrid.Columns>

      </DataGrid>
  </Grid>

4.6 后台代码

  /// <summary>
  /// DataGridMergerCell.xaml 的交互逻辑
  /// </summary>
  [INotifyPropertyChanged]
  public partial class DataGridMergerCell : Window
  {
      public DataGridMergerCell()
      {
          InitializeComponent();
          this.Loaded += DataGridMergerCell_Loaded;
      }

      private List<EquipmentInfo> EquipmentInfosList = new();
      private async void DataGridMergerCell_Loaded(object sender, RoutedEventArgs e)
      {
          // 模拟数据
          var list = new EquipmentInfo[]
          {
              new EquipmentInfo{ DepartmentName="生产部", EquipmentType="加工设备", EquipmentName="车床", EquipmentStatus="正常" },
              new EquipmentInfo{ DepartmentName="生产部", EquipmentType="加工设备", EquipmentName="钻床", EquipmentStatus="正常" },
              new EquipmentInfo{ DepartmentName="加工部", EquipmentType="切割设备", EquipmentName="激光切割机", EquipmentStatus="异常" },
              new EquipmentInfo{ DepartmentName="加工部", EquipmentType="切割设备", EquipmentName="水刀切割机", EquipmentStatus="异常" },
              new EquipmentInfo{ DepartmentName="维修部", EquipmentType="焊接设备", EquipmentName="MIG焊机", EquipmentStatus="正常" },
              new EquipmentInfo{ DepartmentName="维修部", EquipmentType="焊接设备", EquipmentName="TIG焊机", EquipmentStatus="正常" },
              new EquipmentInfo{ DepartmentName="维修部", EquipmentType="焊接设备", EquipmentName="电弧焊机", EquipmentStatus="异常" },
          };
          this.EquipmentInfosList = list.OrderBy(x => x.DepartmentName).ToList();
          var view = CollectionViewSource.GetDefaultView(EquipmentInfosList);
          view.GroupDescriptions.Clear();
          view.GroupDescriptions.Add(new PropertyGroupDescription("DepartmentName"));
          await Task.Delay(500);
          this.datagrid.ItemsSource = view.Groups;
          view.Refresh();
      }

      [ObservableProperty]
      private ObservableCollection<EquipmentInfo> equipmentInfosCollection = new();

      private void btnLoadData_Click(object sender, RoutedEventArgs e)
      {
          var view = CollectionViewSource.GetDefaultView(EquipmentInfosList);
          view.GroupDescriptions.Clear();
          view.GroupDescriptions.Add(new PropertyGroupDescription("DepartmentName"));
          this.datagrid.ItemsSource = view.Groups;
          view.Refresh();
      }
  }

4.7注意要点以及效果

        对数据源的处理很关键,通过将数据集进行分组,对于首列的显示可以直接进行绑定Name。对于其它列,在CellTemplate中嵌入DataGrid控件类进行实现合并单元格,注意这里的DataGrid的数据源需要进行分组操作,通过将ReadOnlyObservableCollection(通过ICollectionView的Groups属性拿到)进行转换成ObservableCollection,可以对其进行分组操作(DataGridCellDataSourceConverter类),之后还需要进行Cell的高度计算(DataGridCellHeightConverter),已确保水平线可以对齐显示。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值