0


WPF DataGrid table表格强大功能,样式的实现之合计栏

最近准备做几期wpf datagrid优化功能的博客,包括合计栏,标题头带搜索功能,标题头带感叹号的提示框,ui优化等等.本期展示合计栏,后续再添加其他功能代码

1.效果展示

实现wpf datagrid的合计栏,标题头搜索,分页,自定义滚动条, 话不多说 先上效果
合计栏效果展示
在这里插入图片描述
标题头筛选UI还未调整,请忽略

2.合计栏实现流程及代码

(1)重写datagrid样式,在datagrid底部添加一个ItemsControl,用于展示合计的项目同时用ScrollViewer包裹ItemsControl,使之能跟随滚动条滚动
<Style x:Key="DesignTotalDataGrid" TargetType="{x:Type controls:DataGrid}"><Setter Property="IsReadOnly" Value="True"/><Setter Property="Background" Value="#FFFAFAFA"/><Setter Property="Foreground" Value="#DD000000"/><Setter Property="BorderBrush" Value="#1F000000"/><Setter Property="BorderThickness" Value="1"/><Setter Property="AutoGenerateColumns" Value="False"/><Setter Property="CanUserAddRows" Value="False"/><Setter Property="FontSize" Value="13"/><Setter Property="GridLinesVisibility" Value="Horizontal"/><Setter Property="HorizontalGridLinesBrush"><Setter.Value><MultiBinding Converter="{StaticResource RemoveAlphaBrushConverter}"><Binding Path="BorderBrush" RelativeSource="{RelativeSource Self}"/><Binding Path="Background" RelativeSource="{RelativeSource Self}"/></MultiBinding></Setter.Value></Setter><Setter Property="VerticalGridLinesBrush" Value="{Binding HorizontalGridLinesBrush, RelativeSource={RelativeSource Self}}"/><Setter Property="RowDetailsVisibilityMode" Value="VisibleWhenSelected"/><Setter Property="HeadersVisibility" Value="Column"/><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 controls:DataGrid}"><Border
                        Padding="{TemplateBinding Padding}"
                        Background="{TemplateBinding Background}"
                        BorderBrush="{TemplateBinding BorderBrush}"
                        BorderThickness="{TemplateBinding BorderThickness}"
                        CornerRadius="{TemplateBinding assist:DataGridAssist.CornerRadius}"
                        SnapsToDevicePixels="True"><Grid><Grid.RowDefinitions><RowDefinition/><RowDefinition Height="auto"/></Grid.RowDefinitions><ScrollViewer x: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><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 x:Name="PART_ColumnHeadersPresenter"/></Border><ScrollContentPresenter
                                            x:Name="PART_ScrollContentPresenter"
                                            Grid.Row="1"
                                            Grid.Column="0"
                                            Grid.ColumnSpan="2"
                                            CanContentScroll="{TemplateBinding CanContentScroll}"/><ScrollBar
                                            x:Name="PART_VerticalScrollBar"
                                            Grid.Row="1"
                                            Style="{StaticResource ScrollBarStyle}"
                                            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="3" Grid.Column="1"><Grid.ColumnDefinitions><ColumnDefinition Width="{Binding NonFrozenColumnsViewportHorizontalOffset, RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}"/><ColumnDefinition Width="*"/></Grid.ColumnDefinitions><ScrollBar
                                                x:Name="PART_HorizontalScrollBar"
                                                Grid.Column="1"
                                                Maximum="{TemplateBinding ScrollableWidth}"
                                                Orientation="Horizontal"
                                                Style="{StaticResource ScrollBarStyle}"
                                                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 BorderThickness="0 1 0 0" BorderBrush="#1F000000"  Grid.Row="1"  Effect="{StaticResource EffectShadow2}"><Grid><ScrollViewer x:Name="scrollViewer" HorizontalScrollBarVisibility="Hidden" VerticalScrollBarVisibility="Hidden"><ItemsControl x:Name="itemsControl"><ItemsControl.ItemsPanel><ItemsPanelTemplate><StackPanel Orientation="Horizontal" VerticalAlignment="Center"/></ItemsPanelTemplate></ItemsControl.ItemsPanel><ItemsControl.ItemTemplate><DataTemplate><Border  Height="{Binding RelativeSource={RelativeSource Self}, Path=(assist:DataGridAssist.CellHeight)}"  Width="{Binding Width}"><TextBlock Text="{Binding Content}" VerticalAlignment="Center" HorizontalAlignment="Center" Margin="3 0"/></Border></DataTemplate></ItemsControl.ItemTemplate></ItemsControl></ScrollViewer></Grid></Border></Grid></Border></ControlTemplate></Setter.Value></Setter><Style.Triggers><MultiTrigger><MultiTrigger.Conditions><Condition Property="IsGrouping" Value="true"/></MultiTrigger.Conditions><Setter Property="ScrollViewer.CanContentScroll" Value="false"/></MultiTrigger></Style.Triggers></Style>
(2)合计栏中每一项宽度的计算

每一项宽度的计算是一个重点,因为每一项的宽度是随内容变化的,同时用户可以拖拽改变每一列的宽度,每次宽度的改变,都得重新计算,这里请注意Border的宽度 Width=“{Binding Width}”,回到cs代码 进行逻辑实现

publicstaticreadonlyDependencyProperty TotalItemsProperty =
            DependencyProperty.Register("TotalItems",typeof(object),typeof(DataGrid),newPropertyMetadata(null, TotalItemsCallBack));publicobject OriginItemsSource {get;set;}publicobject ItemsSources
        {get{return(object)GetValue(ItemsSourcesProperty);}set{SetValue(ItemsSourcesProperty,value);}}

以上代码(来自DataGrid自定义样式类)是合计栏的数据来源,是一个对象,通过后端接口返回的,当然,也可以由前端自己合计,对TotalItems进行赋值即可,赋值方式如下(来自ViewModel业务代码)
在这里插入图片描述

(3)TotalItemsCallBack(合计项赋值后回调)代码分析
privatestaticvoidTotalItemsCallBack(DependencyObject d,DependencyPropertyChangedEventArgs e){DataGrid dataGrid = d asDataGrid;if(e.NewValue !=null&& dataGrid.itemsControl !=null){if(e.NewValue isList<string> totalItems){//数组和对象都要转为数组;//排序问题处理
                    System.Threading.Tasks.Task.Factory.StartNew(()=>{
                        System.Threading.Thread.Sleep(1000);
                        Application.Current.Dispatcher.BeginInvoke((Action)(()=>{List<DataGridTotal> dataGrids =newList<DataGridTotal>();for(int i =0; i < dataGrid.Columns.Count; i++){if(dataGrid.Columns[i].Visibility == Visibility.Visible){if(totalItems !=null&& totalItems.Count >= i +1&& totalItems[i]!=null){
                                        dataGrids.Add(newDataGridTotal{ Width = dataGrid.Columns[i].ActualWidth, Content = totalItems[i], Index = dataGrid.Columns[i].DisplayIndex });}else{
                                        dataGrids.Add(newDataGridTotal{ Width = dataGrid.Columns[i].ActualWidth, Content ="-", Index = dataGrid.Columns[i].DisplayIndex });}}}
                            dataGrids = dataGrids.OrderBy(x => x.Index).ToList();
                            dataGrid.itemsControl.ItemsSource = dataGrids;}));});}elseif(e.NewValue isobject obj){
                    System.Threading.Tasks.Task.Factory.StartNew(()=>{
                        System.Threading.Thread.Sleep(1000);
                        Application.Current.Dispatcher.BeginInvoke((Action)(()=>{List<DataGridTotal> dataGrids =newList<DataGridTotal>();for(int i =0; i < dataGrid.Columns.Count; i++){if(dataGrid.Columns[i].Visibility == Visibility.Visible){var content ="-";if(obj.GetType().GetProperty(dataGrid.Columns[i].SortMemberPath)!=null){
                                        content = obj.GetType().GetProperty(dataGrid.Columns[i].SortMemberPath).GetValue(obj,null)?.ToString();}
                                    dataGrids.Add(newDataGridTotal{ Width = dataGrid.Columns[i].ActualWidth, Content =string.IsNullOrEmpty(content)?"-": content, Index = dataGrid.Columns[i].DisplayIndex });}}
                            dataGrids = dataGrids.OrderBy(x => x.Index).ToList();
                            dataGrid.itemsControl.ItemsSource = dataGrids;}));});}}else{
                System.Threading.Tasks.Task.Factory.StartNew(()=>{
                    System.Threading.Thread.Sleep(1000);
                    Application.Current.Dispatcher.BeginInvoke((Action)(()=>{List<DataGridTotal> dataGrids =newList<DataGridTotal>();for(int i =0; i < dataGrid.Columns.Count; i++){if(dataGrid.Columns[i].Visibility == Visibility.Visible){
                                dataGrids.Add(newDataGridTotal{ Width = dataGrid.Columns[i].ActualWidth, Content ="-", Index = dataGrid.Columns[i].DisplayIndex });}}
                        dataGrids = dataGrids.OrderBy(x => x.Index).ToList();
                        dataGrid.itemsControl.ItemsSource = dataGrids;}));});}}

TotalItems赋值时可以是数组,也可以是对象,数组的话需要按照顺序从左到右一次返回;
Width = dataGrid.Columns[i].ActualWidth是宽度的首次计算;
Index = dataGrid.Columns[i].DisplayIndex 是排序的顺序,确保合计的项不会错位;
DataGridTotal类如下:

publicclassDataGridTotal{/// <summary>/// 内容/// </summary>publicstring Content {get;set;}/// <summary>/// 宽度/// </summary>publicdouble Width {get;set;}/// <summary>/// 排序/// </summary>publicint Index {get;set;}/// <summary>/// 标题头/// </summary>publicstring Header {get;set;}}

加入异步是为了减少前端自己合计 数据量大时的卡顿效果

(4)用户拖动标题头,顺序发生改变后逻辑处理

对合计栏的排序进行重新计算

privatevoidDataGrid_ColumnDisplayIndexChanged(object sender,DataGridColumnEventArgs e){if(TotalItems !=null){List<DataGridTotal> dataGrids =newList<DataGridTotal>();if(TotalItems isList<string> items){for(int i =0; i < Columns.Count; i++){if(Columns[i].Visibility == Visibility.Visible){
                            dataGrids.Add(newDataGridTotal{ Width = Columns[i].ActualWidth, Content = items[i], Index = Columns[i].DisplayIndex });}}}elseif(TotalItems isobject obj){for(int i =0; i < Columns.Count; i++){if(Columns[i].Visibility == Visibility.Visible){var content ="-";if(obj.GetType().GetProperty(Columns[i].SortMemberPath)!=null){
                                content = obj.GetType().GetProperty(Columns[i].SortMemberPath).GetValue(obj,null)?.ToString();}
                            dataGrids.Add(newDataGridTotal{ Width = Columns[i].ActualWidth, Content =string.IsNullOrEmpty(content)?"-":content, Index = Columns[i].DisplayIndex });}}}

                
                dataGrids = dataGrids.OrderBy(x => x.Index).ToList();
                itemsControl.ItemsSource = dataGrids;}else{List<DataGridTotal> dataGrids =newList<DataGridTotal>();for(int i =0; i < Columns.Count; i++){if(Columns[i].Visibility == Visibility.Visible){
                        dataGrids.Add(newDataGridTotal{ Width = Columns[i].ActualWidth, Content ="-", Index = Columns[i].DisplayIndex });}}
                dataGrids = dataGrids.OrderBy(x => x.Index).ToList();
                itemsControl.ItemsSource = dataGrids;}}
(5)合计栏宽度自适应

内容发生改变,列宽自动撑开后合计栏宽度的自适应,用户拖动列的宽度后,合计栏的自适应

privatevoidOwnScrrow_ScrollChanged(object sender,ScrollChangedEventArgs e){/*
            Console.WriteLine("----------------------------");
            Console.WriteLine("ScrollableWidth:"+ownScrrow.ScrollableWidth);
            Console.WriteLine("ExtentWidth:"+ownScrrow.ExtentWidth);
            Console.WriteLine("VerticalOffset:" + ownScrrow.VerticalOffset);
            Console.WriteLine("HorizontalOffset:" + ownScrrow.HorizontalOffset);
            */DataGrid_ColumnDisplayIndexChanged(null,null);/*
            if (ownScrrow.ScrollableWidth != horOffset)
            {
                horOffset = ownScrrow.ScrollableWidth;
                //宽度发生改变 通知滚动条重新渲染
                Console.WriteLine(horOffset);
                DataGrid_ColumnDisplayIndexChanged(null, null);
            } 
            */
            scrollViewer.ScrollToHorizontalOffset((sender asScrollViewer).HorizontalOffset);}

3.性能分析

windows xp(客户那里找来的,估计年龄比我还大) 256m内存运行流畅,高于此配置均能完美运行

4.代码

gitee地址

标签: wpf c# 前端

本文转载自: https://blog.csdn.net/qq_40127027/article/details/128861564
版权归原作者 Ryannn_NN 所有, 如有侵权,请联系我们删除。

“WPF DataGrid table表格强大功能,样式的实现之合计栏”的评论:

还没有评论