首页 > 其他分享 >WPF多表头表格实现

WPF多表头表格实现

时间:2024-04-19 14:12:09浏览次数:24  
标签:控件 表格 表头 WPF ListView 模板

前言

多表头表格是一个常见的业务需求,然而WPF中却没有默认实现这个功能,得益于WPF强大的控件模板设计,我们可以通过修改控件模板的方式自己实现它。

一、需求分析

   下图为一个典型的统计表格,统计1-12月的数据。

      此时我们有一个需求,需要将月份按季度划分,以便能够直观地看到季度统计数据,以下为该需求的最终效果。

      通过上图分析我们可以看出,我们需要在每个月份上设置一个值来标记它是属于哪一个季度,并且在列上面把它显示出来。

二、程序设计

     WPF所有控件中最贴近需求的控件是DataGrid和ListView,而DataGrid除了基本的表格功能外还有新增行、编辑行、删除行等功能,为了获得更高的性能,我们这里使用更加轻量级的ListView来实现多表头表格功能。

      下图为ListView控件的运行效果,我们可以分析一下ListView控件的模板,看看如何来添加多表头功能。

       以下代码为ListView的控件模板。

<SolidColorBrush x:Key="ListBorder" Color="#828790"/>
<Style x:Key="{x:Static GridView.GridViewScrollViewerStyleKey}" TargetType="{x:Type ScrollViewer}">
    <Setter Property="Focusable" Value="false"/>
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type ScrollViewer}">
                <Grid SnapsToDevicePixels="true" Background="{TemplateBinding Background}">
                    <Grid.RowDefinitions>
                        <RowDefinition Height="*"/>
                        <RowDefinition Height="Auto"/>
                    </Grid.RowDefinitions>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="*"/>
                        <ColumnDefinition Width="Auto"/>
                    </Grid.ColumnDefinitions>
                    <DockPanel Margin="{TemplateBinding Padding}">
                        <ScrollViewer VerticalScrollBarVisibility="Hidden" HorizontalScrollBarVisibility="Hidden" Focusable="false" DockPanel.Dock="Top">
                            <GridViewHeaderRowPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" Margin="2,0,2,0" ColumnHeaderTemplateSelector="{Binding TemplatedParent.View.ColumnHeaderTemplateSelector, RelativeSource={RelativeSource TemplatedParent}}" Columns="{Binding TemplatedParent.View.Columns, RelativeSource={RelativeSource TemplatedParent}}" ColumnHeaderTemplate="{Binding TemplatedParent.View.ColumnHeaderTemplate, RelativeSource={RelativeSource TemplatedParent}}" ColumnHeaderContextMenu="{Binding TemplatedParent.View.ColumnHeaderContextMenu, RelativeSource={RelativeSource TemplatedParent}}" ColumnHeaderStringFormat="{Binding TemplatedParent.View.ColumnHeaderStringFormat, RelativeSource={RelativeSource TemplatedParent}}" ColumnHeaderToolTip="{Binding TemplatedParent.View.ColumnHeaderToolTip, RelativeSource={RelativeSource TemplatedParent}}" ColumnHeaderContainerStyle="{Binding TemplatedParent.View.ColumnHeaderContainerStyle, RelativeSource={RelativeSource TemplatedParent}}" AllowsColumnReorder="{Binding TemplatedParent.View.AllowsColumnReorder, RelativeSource={RelativeSource TemplatedParent}}"/>
                        </ScrollViewer>
                        <ScrollContentPresenter x:Name="PART_ScrollContentPresenter" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" KeyboardNavigation.DirectionalNavigation="Local" Content="{TemplateBinding Content}" ContentTemplate="{TemplateBinding ContentTemplate}" CanContentScroll="{TemplateBinding CanContentScroll}"/>
                    </DockPanel>
                    <ScrollBar x:Name="PART_HorizontalScrollBar" ViewportSize="{TemplateBinding ViewportWidth}" Value="{Binding HorizontalOffset, Mode=OneWay, RelativeSource={RelativeSource TemplatedParent}}" Visibility="{TemplateBinding ComputedHorizontalScrollBarVisibility}" Grid.Row="1" Orientation="Horizontal" Minimum="0.0" Maximum="{TemplateBinding ScrollableWidth}" Cursor="Arrow"/>
                    <ScrollBar x:Name="PART_VerticalScrollBar" ViewportSize="{TemplateBinding ViewportHeight}" Value="{Binding VerticalOffset, Mode=OneWay, RelativeSource={RelativeSource TemplatedParent}}" Visibility="{TemplateBinding ComputedVerticalScrollBarVisibility}" Orientation="Vertical" Minimum="0.0" Maximum="{TemplateBinding ScrollableHeight}" Grid.Column="1" Cursor="Arrow"/>
                    <DockPanel Grid.Row="1" LastChildFill="false" Grid.Column="1" Background="{Binding Background, ElementName=PART_VerticalScrollBar}">
                        <Rectangle Width="1" Visibility="{TemplateBinding ComputedVerticalScrollBarVisibility}" Fill="White" DockPanel.Dock="Left"/>
                        <Rectangle Visibility="{TemplateBinding ComputedHorizontalScrollBarVisibility}" Height="1" Fill="White" DockPanel.Dock="Top"/>
                    </DockPanel>
                </Grid>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>
<Style x:Key="ListViewStyle1" TargetType="{x:Type ListView}">
    <Setter Property="Background" Value="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"/>
    <Setter Property="BorderBrush" Value="{StaticResource ListBorder}"/>
    <Setter Property="BorderThickness" Value="1"/>
    <Setter Property="Foreground" Value="#FF042271"/>
    <Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Auto"/>
    <Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Auto"/>
    <Setter Property="ScrollViewer.CanContentScroll" Value="true"/>
    <Setter Property="ScrollViewer.PanningMode" Value="Both"/>
    <Setter Property="Stylus.IsFlicksEnabled" Value="False"/>
    <Setter Property="VerticalContentAlignment" Value="Center"/>
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type ListView}">
                <Themes:ListBoxChrome x:Name="Bd" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" RenderMouseOver="{TemplateBinding IsMouseOver}" RenderFocused="{TemplateBinding IsKeyboardFocusWithin}" SnapsToDevicePixels="true">
                    <ScrollViewer Padding="{TemplateBinding Padding}" Style="{DynamicResource {x:Static GridView.GridViewScrollViewerStyleKey}}">
                        <ItemsPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
                    </ScrollViewer>
                </Themes:ListBoxChrome>
                <ControlTemplate.Triggers>
                    <Trigger Property="IsEnabled" Value="false">
                        <Setter Property="Background" TargetName="Bd" Value="{DynamicResource {x:Static SystemColors.ControlBrushKey}}"/>
                    </Trigger>
                    <MultiTrigger>
                        <MultiTrigger.Conditions>
                            <Condition Property="IsGrouping" Value="true"/>
                            <Condition Property="VirtualizingPanel.IsVirtualizingWhenGrouping" Value="false"/>
                        </MultiTrigger.Conditions>
                        <Setter Property="ScrollViewer.CanContentScroll" Value="false"/>
                    </MultiTrigger>
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

 通过以上代码可以得知,ListView控件模板外层为一个ScrollViewer控件,ScrollViewer中包含了一个ItemsPresenter控件,而列头就在ScrollViewer控件模板中,GridViewHeaderRowPresenter就是列头的最终呈现控件,我们只需要在GridViewHeaderRowPresenter的上面再放一个呈现列头的控件就可以实现多列头功能。

三、代码实现

3.1 定义一个附加属性,用于设置列的分组。

public static class ListViewExtensions
    {
        #region Group
        public static string GetGroup(DependencyObject obj)
        {
            return (string)obj.GetValue(GroupProperty);
        }

        public static void SetGroup(DependencyObject obj, string value)
        {
            obj.SetValue(GroupProperty, value);
        }

        public static readonly DependencyProperty GroupProperty =
            DependencyProperty.RegisterAttached("Group", typeof(string), typeof(ListViewExtensions));

        #endregion
    }

3.2 设置列分组。

<ListView>
    <ListView.View>
        <GridView>
            <GridViewColumn Extensions:ListViewExtensions.Group="Group1" DisplayMemberBinding="{Binding Property1}">
                <GridViewColumn.Header>
                    <GridViewColumnHeader>Property1</GridViewColumnHeader>
                </GridViewColumn.Header>
            </GridViewColumn>
            <GridViewColumn Extensions:ListViewExtensions.Group="Group1" DisplayMemberBinding="{Binding Property2}">
                <GridViewColumn.Header>
                    <GridViewColumnHeader>Property2</GridViewColumnHeader>
                </GridViewColumn.Header>
            </GridViewColumn>
            <GridViewColumn Extensions:ListViewExtensions.Group="Group2" DisplayMemberBinding="{Binding Property3}">
                <GridViewColumn.Header>
                    <GridViewColumnHeader>Property3</GridViewColumnHeader>
                </GridViewColumn.Header>
            </GridViewColumn>
            <GridViewColumn Extensions:ListViewExtensions.Group="Group2" DisplayMemberBinding="{Binding Property4}">
                <GridViewColumn.Header>
                    <GridViewColumnHeader>Property4</GridViewColumnHeader>
                </GridViewColumn.Header>
            </GridViewColumn>
            <GridViewColumn Extensions:ListViewExtensions.Group="Group2" DisplayMemberBinding="{Binding Property5}">
                <GridViewColumn.Header>
                    <GridViewColumnHeader>Property5</GridViewColumnHeader>
                </GridViewColumn.Header>
            </GridViewColumn>
        </GridView>
    </ListView.View>
</ListView>

3.3 写一个继承自GridViewHeaderRowPresenter类的自定义控件(此处命名为GridViewGroupHeaderRowPresenter),用于处理分组列,该控件通过读取GridViewColumn设置的Extensions:ListViewExtensions.Group属性来创建分组列,并负责处理分组列与普通列的宽度分配和同步。

3.4 将GridViewGroupHeaderRowPresenter添加到ListView模板中,以下为关键代码。

<StackPanel Orientation="Vertical">
   <local:GridViewGroupHeaderRowPresenter OriginalColumns="{Binding TemplatedParent.View.Columns, RelativeSource={RelativeSource Mode=TemplatedParent}}" />
   <GridViewHeaderRowPresenter AllowsColumnReorder="{Binding TemplatedParent.View.AllowsColumnReorder, RelativeSource={RelativeSource Mode=TemplatedParent}}"
                                        ColumnHeaderContainerStyle="{Binding TemplatedParent.View.ColumnHeaderContainerStyle, RelativeSource={RelativeSource Mode=TemplatedParent}}"
                                        ColumnHeaderContextMenu="{Binding TemplatedParent.View.ColumnHeaderContextMenu, RelativeSource={RelativeSource Mode=TemplatedParent}}"
                                        ColumnHeaderStringFormat="{Binding TemplatedParent.View.ColumnHeaderStringFormat, RelativeSource={RelativeSource Mode=TemplatedParent}}"
                                        ColumnHeaderTemplate="{Binding TemplatedParent.View.ColumnHeaderTemplate, RelativeSource={RelativeSource Mode=TemplatedParent}}"
                                        ColumnHeaderTemplateSelector="{Binding TemplatedParent.View.ColumnHeaderTemplateSelector, RelativeSource={RelativeSource Mode=TemplatedParent}}"
                                        ColumnHeaderToolTip="{Binding TemplatedParent.View.ColumnHeaderToolTip, RelativeSource={RelativeSource Mode=TemplatedParent}}"
                                        Columns="{Binding TemplatedParent.View.Columns, RelativeSource={RelativeSource Mode=TemplatedParent}}"
                                        SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
</StackPanel>

      至此开发完成,以下为运行效果。

四、自定义外观

      该控件基于ListView标准模板开发,可以在模板中自由修改控件外观,也可以使用第三方UI库,以下为使用MaterialDesign库的效果。

 

标签:控件,表格,表头,WPF,ListView,模板
From: https://www.cnblogs.com/qushi2020/p/18145779

相关文章

  • WPF项目使用日志
    提问WPF项目如何使用日志回答引入nugetlog4net加入配置特性[assembly:ThemeInfo(ResourceDictionaryLocation.None,//wherethemespecificresourcedictionariesarelocated//(usedifaresourceisnotfoundinthepage,//orapplicationresourcedict......
  • WPF/C#:让绘制的图形可以被选中并将信息显示在ListBox中
    实现的效果如果你对此感兴趣,可以接着往下阅读。实现过程绘制矩形比如说我想绘制一个3行4列的表格:privatevoidButton_Click_DrawRect(objectsender,RoutedEventArgse){intRow=3;intCol=4;for(inti=0;i<Row;i++){......
  • 界面组件库DevExpress Office File API(WinForms & WPF)v24.1新功能预览
    本文描述了界面组件库DevExpress的OfficeFileAPI(WinForms&WPF)和受Office启发的控件在v24.1中发布的一些功能,并详细介绍了我们当前的抢先体验预览版本v24.1中的内容。DevExpressWPF拥有120+个控件和库,将帮助您交付满足甚至超出企业需求的高性能业务应用程序。通过DevExpress......
  • 【高级RAG技巧】在大模型知识库问答中增强文档分割与表格提取
    前言文档分割是一项具有挑战性的任务,它是任何知识库问答系统的基础。高质量的文档分割结果对于显著提升问答效果至关重要,但是目前大多数开源库的处理能力有限。这些开源的库或者方法缺点大致可以罗列如下:只能处理文本,无法提取表格中的内容缺乏有效的分割策略,要么是一整个文档......
  • 【转载】清空WPF自带样式,以及透明按钮
    原文:https://www.cnblogs.com/Cindys/archive/2012/09/11/2680501.html空样式按钮<Stylex:Key="EmptyButtonStyle"TargetType="Button">           <SetterProperty="Padding"Value="0"/>           <SetterProper......
  • 【转】[C#][WPF] GridControl 列宽控制
    在设置DevExpress里的GridControl自动列宽时,有两个方式:view.BestFitColumn(gridColumn);view.BestFitColumns();但我想要达到这样的效果:1、加载配置,读取列宽2、未配置列宽的列自动列宽发现可以这样组合://如果已配置列宽,自动列宽就是配置的宽度if(gridColumn.Widt......
  • pageoffice在线打开word文件生成表格
    转载:数据区域生成表格#数据区域生成表格查看本示例演示效果本示例关键代码的编写位置Vue+Springboot注意本文中展示的代码均为关键代码,复制粘贴到您的项目中,按照实际的情况,例如文档路径,用户名等做适当修改即可使用。在实际项目的开发中会遇到这样的需求:要求在生成word文件......
  • 界面组件Telerik UI for WPF 2024 Q1新版亮点 - 全新DateRangePicker组件
    TelerikUIforWPF拥有超过100个控件来创建美观、高性能的桌面应用程序,同时还能快速构建企业级办公WPF应用程序。UIforWPF支持MVVM、触摸等,创建的应用程序可靠且结构良好,非常容易维护,其直观的API将无缝地集成VisualStudio工具箱中。本文将介绍界面组件TelerikUIforWPF在今......
  • Python Flask+Pandas读取excel显示到html网页: CSS控制表格样式、表头文字居中
    前言全局说明CSS控制表格样式一、安装flask模块二、引用模块三、启动服务模块安装、引用模块、启动Web服务方法,参考下面链接文章:https://www.cnblogs.com/wutou/p/17963563Pandas安装https://www.cnblogs.com/wutou/p/17811839.htmlPandas官方API说明https://pand......
  • WPF 虚拟化
    1通用部分可以参考 优化控件性能-WPF.NETFramework|MicrosoftLearn主要的设置是<SetterProperty="VirtualizingPanel.IsVirtualizing"Value="True"/><SetterProperty="VirtualizingPanel.VirtualizationMode"Value="Recycling&quo......