当前位置:首页 » 《随便一记》 » 正文

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

7 人参与  2024年02月19日 15:31  分类 : 《随便一记》  评论

点击全文阅读


最近准备做几期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代码 进行逻辑实现

public static readonly DependencyProperty TotalItemsProperty =            DependencyProperty.Register("TotalItems", typeof(object), typeof(DataGrid), new PropertyMetadata(null, TotalItemsCallBack));        public object OriginItemsSource { get; set; }        public  object ItemsSources        {            get { return (object)GetValue(ItemsSourcesProperty); }            set { SetValue(ItemsSourcesProperty, value); }        }

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

(3)TotalItemsCallBack(合计项赋值后回调)代码分析
        private static void TotalItemsCallBack(DependencyObject d, DependencyPropertyChangedEventArgs e)        {            DataGrid dataGrid = d as DataGrid;            if (e.NewValue != null && dataGrid.itemsControl != null)            {                if (e.NewValue is List<string> totalItems)                {                    //数组和对象都要转为数组;                    //排序问题处理                    System.Threading.Tasks.Task.Factory.StartNew(() =>                    {                        System.Threading.Thread.Sleep(1000);                        Application.Current.Dispatcher.BeginInvoke((Action)(() =>                        {                            List<DataGridTotal> dataGrids = new List<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(new DataGridTotal { Width = dataGrid.Columns[i].ActualWidth, Content = totalItems[i], Index = dataGrid.Columns[i].DisplayIndex });                                    }                                    else                                    {                                        dataGrids.Add(new DataGridTotal { Width = dataGrid.Columns[i].ActualWidth, Content = "-", Index = dataGrid.Columns[i].DisplayIndex });                                    }                                }                            }                            dataGrids = dataGrids.OrderBy(x => x.Index).ToList();                            dataGrid.itemsControl.ItemsSource = dataGrids;                        }));                    });                }                else if(e.NewValue is object obj){                    System.Threading.Tasks.Task.Factory.StartNew(() =>                    {                        System.Threading.Thread.Sleep(1000);                        Application.Current.Dispatcher.BeginInvoke((Action)(() =>                        {                            List<DataGridTotal> dataGrids = new List<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(new DataGridTotal { 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 = new List<DataGridTotal>();                        for (int i = 0; i < dataGrid.Columns.Count; i++)                        {                            if (dataGrid.Columns[i].Visibility == Visibility.Visible)                            {                                dataGrids.Add(new DataGridTotal { 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类如下:

public class DataGridTotal    {        /// <summary>        /// 内容        /// </summary>        public string Content { get; set; }        /// <summary>        /// 宽度        /// </summary>        public double Width { get; set; }        /// <summary>        /// 排序        /// </summary>        public int Index { get; set; }         /// <summary>        /// 标题头        /// </summary>        public string Header { get; set; }    }

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

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

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

        private void DataGrid_ColumnDisplayIndexChanged(object sender, DataGridColumnEventArgs e)        {            if (TotalItems != null)            {                List<DataGridTotal> dataGrids = new List<DataGridTotal>();                if (TotalItems is List<string> items)                {                    for (int i = 0; i < Columns.Count; i++)                    {                        if (Columns[i].Visibility == Visibility.Visible)                        {                            dataGrids.Add(new DataGridTotal { Width = Columns[i].ActualWidth, Content = items[i], Index = Columns[i].DisplayIndex });                        }                    }                }                else if(TotalItems is object 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(new DataGridTotal { 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 = new List<DataGridTotal>();                for (int i = 0; i < Columns.Count; i++)                {                    if (Columns[i].Visibility == Visibility.Visible)                    {                        dataGrids.Add(new DataGridTotal { Width = Columns[i].ActualWidth, Content = "-", Index = Columns[i].DisplayIndex });                    }                }                dataGrids = dataGrids.OrderBy(x => x.Index).ToList();                itemsControl.ItemsSource = dataGrids;            }                    }
(5)合计栏宽度自适应

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

private void OwnScrrow_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 as ScrollViewer).HorizontalOffset);        }

3.性能分析

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

4.代码

gitee地址


点击全文阅读


本文链接:http://zhangshiyu.com/post/69082.html

<< 上一篇 下一篇 >>

  • 评论(0)
  • 赞助本站

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

关于我们 | 我要投稿 | 免责申明

Copyright © 2020-2022 ZhangShiYu.com Rights Reserved.豫ICP备2022013469号-1