WPF实现树形表格控件的示例代码

 更新时间:2024年04月02日 11:05:56   作者:趋时软件  
这篇文章主要介绍了如何利用WPF框架实现树形表格控件,该控件不仅能够有效地展示复杂的层级数据,还能够提供丰富的个性化定制选项,感兴趣的可以了解下
(福利推荐:【腾讯云】服务器最新限时优惠活动,云服务器1核2G仅99元/年、2核4G仅768元/3年,立即抢购>>>:9i0i.cn/qcloud

(福利推荐:你还在原价购买阿里云服务器?现在阿里云0.8折限时抢购活动来啦!4核8G企业云服务器仅2998元/3年,立即抢购>>>:9i0i.cn/aliyun

前言

本文将探讨如何利用WPF框架实现树形表格控件,该控件不仅能够有效地展示复杂的层级数据,还能够提供丰富的个性化定制选项。我们将介绍如何使用WPF提供的控件、模板、布局、数据绑定等技术来构建这样一个树形表格。

一、运行效果

1.1默认样式

1.2 自定义样式

二、代码实现

2.1 创建自定义控件(TreeListView)

新建一个继承自TreeView的控件,并定义一个类型为ViewBase的View依赖属性,用于在代码中指定列。

public class TreeListView : TreeView
{
        public ViewBase View
        {
            get { return (ViewBase)GetValue(ViewProperty); }
            set { SetValue(ViewProperty, value); }
        }

        public static readonly DependencyProperty ViewProperty =
            DependencyProperty.Register("View", typeof(ViewBase), typeof(TreeListView));
}

2.2 在TreeListView控件模板中处理列头

为了在TreeListView中显示列头,需要在合适的位置添加GridViewHeaderRowPresenter控件,并在Columns属性上绑定我们之前定义的View.Columns属性。下面我们首先来分析TreeView控件模板的代码。

<ControlTemplate TargetType="{x:Type TreeView}">
    <Border x:Name="Bd" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" SnapsToDevicePixels="true">
        <ScrollViewer x:Name="_tv_scrollviewer_" Background="{TemplateBinding Background}" CanContentScroll="false" Focusable="false" HorizontalScrollBarVisibility="{TemplateBinding ScrollViewer.HorizontalScrollBarVisibility}" Padding="{TemplateBinding Padding}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalScrollBarVisibility="{TemplateBinding ScrollViewer.VerticalScrollBarVisibility}">
            <ItemsPresenter/>
        </ScrollViewer>
    </Border>
    <ControlTemplate.Triggers>
        <Trigger Property="IsEnabled" Value="false">
            <Setter Property="Background" TargetName="Bd" Value="{DynamicResource {x:Static SystemColors.ControlBrushKey}}"/>
        </Trigger>
        <Trigger Property="VirtualizingPanel.IsVirtualizing" Value="true">
            <Setter Property="CanContentScroll" TargetName="_tv_scrollviewer_" Value="true"/>
        </Trigger>
    </ControlTemplate.Triggers>
</ControlTemplate>

通过以上代码我们可以看出,只要将GridViewHeaderRowPresenter控件添加到ScrollViewer控件上面即可实现列头功能,但这样会有一个问题,那就是内容宽度超出控件宽度后,鼠标拖动横向滚动条时列头不会跟随下方的数据列表一起滚动。为解决这个问题我们需要将GridViewHeaderRowPresenter放置到ScrollViewer控件模板中,以下为完整代码。

<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 Background="{TemplateBinding Background}" SnapsToDevicePixels="true">
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="*" />
                        <ColumnDefinition Width="Auto" />
                    </Grid.ColumnDefinitions>
                    <Grid.RowDefinitions>
                        <RowDefinition Height="*" />
                        <RowDefinition Height="Auto" />
                    </Grid.RowDefinitions>

                    <DockPanel Margin="{TemplateBinding Padding}">
                        <ScrollViewer
                                DockPanel.Dock="Top"
                                Focusable="false"
                                HorizontalScrollBarVisibility="Hidden"
                                VerticalScrollBarVisibility="Hidden">
                            <GridViewHeaderRowPresenter
                                    Margin="2,0,2,0"
                                    AllowsColumnReorder="{Binding TemplatedParent.View.AllowsColumnReorder, RelativeSource={RelativeSource TemplatedParent}}"
                                    ColumnHeaderContainerStyle="{Binding TemplatedParent.View.ColumnHeaderContainerStyle, RelativeSource={RelativeSource TemplatedParent}}"
                                    ColumnHeaderContextMenu="{Binding TemplatedParent.View.ColumnHeaderContextMenu, RelativeSource={RelativeSource TemplatedParent}}"
                                    ColumnHeaderStringFormat="{Binding TemplatedParent.View.ColumnHeaderStringFormat, RelativeSource={RelativeSource TemplatedParent}}"
                                    ColumnHeaderTemplate="{Binding TemplatedParent.View.ColumnHeaderTemplate, RelativeSource={RelativeSource TemplatedParent}}"
                                    ColumnHeaderTemplateSelector="{Binding TemplatedParent.View.ColumnHeaderTemplateSelector, RelativeSource={RelativeSource TemplatedParent}}"
                                    ColumnHeaderToolTip="{Binding TemplatedParent.View.ColumnHeaderToolTip, RelativeSource={RelativeSource TemplatedParent}}"
                                    Columns="{Binding TemplatedParent.View.Columns, RelativeSource={RelativeSource TemplatedParent}}"
                                    SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
                        </ScrollViewer>
                        <ScrollContentPresenter
                                x:Name="PART_ScrollContentPresenter"
                                CanContentScroll="{TemplateBinding CanContentScroll}"
                                Content="{TemplateBinding Content}"
                                ContentTemplate="{TemplateBinding ContentTemplate}"
                                KeyboardNavigation.DirectionalNavigation="Local"
                                SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
                    </DockPanel>

                    <ScrollBar
                            x:Name="PART_HorizontalScrollBar"
                            Grid.Row="1"
                            Cursor="Arrow"
                            Maximum="{TemplateBinding ScrollableWidth}"
                            Minimum="0.0"
                            Orientation="Horizontal"
                            ViewportSize="{TemplateBinding ViewportWidth}"
                            Visibility="{TemplateBinding ComputedHorizontalScrollBarVisibility}"
                            Value="{Binding HorizontalOffset, Mode=OneWay, RelativeSource={RelativeSource TemplatedParent}}" />
                    <ScrollBar
                            x:Name="PART_VerticalScrollBar"
                            Grid.Column="1"
                            Cursor="Arrow"
                            Maximum="{TemplateBinding ScrollableHeight}"
                            Minimum="0.0"
                            Orientation="Vertical"
                            ViewportSize="{TemplateBinding ViewportHeight}"
                            Visibility="{TemplateBinding ComputedVerticalScrollBarVisibility}"
                            Value="{Binding VerticalOffset, Mode=OneWay, RelativeSource={RelativeSource TemplatedParent}}" />
                    <DockPanel
                            Grid.Row="1"
                            Grid.Column="1"
                            Background="{Binding Background, ElementName=PART_VerticalScrollBar}"
                            LastChildFill="false">
                        <Rectangle
                                Width="1"
                                DockPanel.Dock="Left"
                                Fill="White"
                                Visibility="{TemplateBinding ComputedVerticalScrollBarVisibility}" />
                        <Rectangle
                                Height="1"
                                DockPanel.Dock="Top"
                                Fill="White"
                                Visibility="{TemplateBinding ComputedHorizontalScrollBarVisibility}" />
                    </DockPanel>
                </Grid>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

<Style TargetType="{x:Type local:TreeListView}">
    <Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Auto" />
    <Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Auto" />
    <Setter Property="ScrollViewer.CanContentScroll" Value="true" />
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type local:TreeListView}">
                <Border BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}">
                    <ScrollViewer Padding="{TemplateBinding Padding}" Style="{StaticResource {x:Static GridView.GridViewScrollViewerStyleKey}}">
                        <ItemsPresenter />
                    </ScrollViewer>
                </Border>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

2.3 在TreeListViewItem模板中处理子项的展开和收缩

新建一个继承自TreeViewItem的类,命名为TreeListViewItem(如有个性化需求,可以在该类中处理),编辑控件模板,在模板中添加以下代码。

<Style TargetType="{x:Type local:TreeListViewItem}">
    <Setter Property="BorderThickness" Value="1" />
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type local:TreeListViewItem}">
                <StackPanel>
                    <Border
                            Name="Bd"
                            Padding="{TemplateBinding Padding}"
                            Background="{TemplateBinding Background}"
                            BorderBrush="{TemplateBinding BorderBrush}"
                            BorderThickness="{TemplateBinding BorderThickness}">
                        <GridViewRowPresenter
                                x:Name="PART_Header"
                                Columns="{Binding RelativeSource={RelativeSource AncestorType=local:TreeListView}, Path=View.Columns}"
                                Content="{TemplateBinding Header}" />
                    </Border>
                    <ItemsPresenter x:Name="ItemsHost" />
                </StackPanel>
                <ControlTemplate.Triggers>
                    <Trigger Property="IsExpanded" Value="false">
                        <Setter TargetName="ItemsHost" Property="Visibility" Value="Collapsed" />
                    </Trigger>
                    <MultiTrigger>
                        <MultiTrigger.Conditions>
                            <Condition Property="HasHeader" Value="false" />
                            <Condition Property="Width" Value="Auto" />
                        </MultiTrigger.Conditions>
                        <Setter TargetName="PART_Header" Property="MinWidth" Value="75" />
                    </MultiTrigger>
                    <MultiTrigger>
                        <MultiTrigger.Conditions>
                            <Condition Property="HasHeader" Value="false" />
                            <Condition Property="Height" Value="Auto" />
                        </MultiTrigger.Conditions>
                        <Setter TargetName="PART_Header" Property="MinHeight" Value="19" />
                    </MultiTrigger>

                    <MultiTrigger>
                        <MultiTrigger.Conditions>
                            <Condition Property="extensions:TreeViewItemExtensions.IsMouseDirectlyOverItem" Value="True" />
                        </MultiTrigger.Conditions>
                        <Setter TargetName="Bd" Property="Background" Value="{StaticResource Item.MouseOver.Background}" />
                        <Setter TargetName="Bd" Property="BorderBrush" Value="{StaticResource Item.MouseOver.Border}" />
                    </MultiTrigger>
                    <MultiTrigger>
                        <MultiTrigger.Conditions>
                            <Condition Property="Selector.IsSelectionActive" Value="False" />
                            <Condition Property="IsSelected" Value="True" />
                        </MultiTrigger.Conditions>
                        <Setter TargetName="Bd" Property="Background" Value="{StaticResource Item.SelectedInactive.Background}" />
                        <Setter TargetName="Bd" Property="BorderBrush" Value="{StaticResource Item.SelectedInactive.Border}" />
                    </MultiTrigger>
                    <MultiTrigger>
                        <MultiTrigger.Conditions>
                            <Condition Property="Selector.IsSelectionActive" Value="True" />
                            <Condition Property="IsSelected" Value="True" />
                        </MultiTrigger.Conditions>
                        <Setter TargetName="Bd" Property="Background" Value="{StaticResource Item.SelectedActive.Background}" />
                        <Setter TargetName="Bd" Property="BorderBrush" Value="{StaticResource Item.SelectedActive.Border}" />
                    </MultiTrigger>
                    <Trigger Property="IsEnabled" Value="False">
                        <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}" />
                    </Trigger>
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

此处的核心在于模板中添加了GridViewRowPresenter控件,并在Columns属性上绑定了我们之前定义的View.Columns属性,这样就可以在每一行上面显示列数据。还有一个关键点是ItemsPresenter,它用于显示子项数据,此处命名为ItemsHost,它由属性触发器中的代码来控件展开和收起。以下是属性触发器代码。

<Trigger Property="IsExpanded" Value="false">
    <Setter TargetName="ItemsHost" Property="Visibility" Value="Collapsed" />
</Trigger>

2.4 在单元格模板中控件子项的展开与收起

为了达到展开和收起的效果,需要在首列的单元格中控制TreeListViewItem的IsExpanded属性。以下为完整代码。

<DataTemplate x:Key="ExpandCellTemplate">
    <DockPanel>
        <ToggleButton
                            x:Name="Expander"
                            Margin="{Binding Path=Level, Converter={StaticResource LevelIndentConverter}, RelativeSource={RelativeSource AncestorType={x:Type TreeListViewItem}}}"
                            ClickMode="Press"
                            IsChecked="{Binding Path=IsExpanded, RelativeSource={RelativeSource AncestorType={x:Type TreeListViewItem}}}"
                            Style="{StaticResource ExpandCollapseToggleStyle}" />
        <TextBlock Text="{Binding Property1}" />
    </DockPanel>
    <DataTemplate.Triggers>
        <DataTrigger Binding="{Binding Path=HasItems, RelativeSource={RelativeSource AncestorType={x:Type TreeListViewItem}}}" Value="False">
            <Setter TargetName="Expander" Property="Visibility" Value="Hidden" />
        </DataTrigger>
    </DataTemplate.Triggers>
</DataTemplate>

其关键代码为

IsChecked="{Binding Path=IsExpanded, RelativeSource={RelativeSource AncestorType={x:Type TreeListViewItem}}}"

2.5 控件使用

<TreeListView ItemsSource="{Binding Collection}">
    <TreeListView.ItemTemplate>
        <HierarchicalDataTemplate ItemsSource="{Binding Collection, IsAsync=True}" />
    </TreeListView.ItemTemplate>
    <TreeListView.View>
        <GridView>
            <GridViewColumn CellTemplate="{StaticResource ExpandCellTemplate}" Header="Property1" />
            <GridViewColumn DisplayMemberBinding="{Binding Property2}" Header="Property2" />
            <GridViewColumn DisplayMemberBinding="{Binding Property3}" Header="Property3" />
            <GridViewColumn DisplayMemberBinding="{Binding Property4}" Header="Property4" />
            <GridViewColumn DisplayMemberBinding="{Binding Property5}" Header="Property5" />
            <GridViewColumn DisplayMemberBinding="{Binding Property6}" Header="Property6" />
            <GridViewColumn DisplayMemberBinding="{Binding Property7}" Header="Property7" />
            <GridViewColumn DisplayMemberBinding="{Binding Property8}" Header="Property8" />
            <GridViewColumn DisplayMemberBinding="{Binding Property9}" Header="Property9" />
            <GridViewColumn DisplayMemberBinding="{Binding Property10}" Header="Property10" />
            <GridViewColumn DisplayMemberBinding="{Binding Property11}" Header="Property11" />
            <GridViewColumn DisplayMemberBinding="{Binding Property12}" Header="Property12" />
        </GridView>
    </TreeListView.View>
</TreeListView>

以上就是WPF实现树形表格控件的示例代码的详细内容,更多关于WPF树形表格控件的资料请关注程序员之家其它相关文章!

相关文章

  • 基于C#实现电脑系统挂机锁

    基于C#实现电脑系统挂机锁

    这篇文章主要为大家详细介绍了如何利用C#实现电脑系统挂机锁,文中的示例代码讲解详细,对我们学习C#有一定的帮助,感兴趣的小伙伴可以跟随小编一起了解一下
    2022-12-12
  • C# 实现绘制PDF嵌套表格案例详解

    C# 实现绘制PDF嵌套表格案例详解

    嵌套表格,顾名思义,就是在一张表格中的特定单元格中再插入一个或者多个表格,本文将为大家介绍C#绘制PDF嵌套表格的代码示例,需要的同学可以参考一下
    2021-11-11
  • C#函数out多个返回值问题

    C#函数out多个返回值问题

    这篇文章主要介绍了C#函数out多个返回值问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-02-02
  • C#使用CefSharp实现内嵌网页详解

    C#使用CefSharp实现内嵌网页详解

    这篇文章主要介绍了C# WPF里怎么使用CefSharp嵌入一个网页,并给出一个简单示例演示C#和网页(JS)的交互实现,感兴趣的小伙伴可以了解一下
    2023-04-04
  • C#实现工厂方法模式

    C#实现工厂方法模式

    这篇文章介绍了C#实现工厂模式的方法,文中通过示例代码介绍的非常详细。对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2022-07-07
  • C#使用linq查询大数据集的方法

    C#使用linq查询大数据集的方法

    这篇文章主要介绍了C#使用linq查询大数据集的方法,涉及C#调用linq进行数据查询的技巧,具有一定参考借鉴价值,需要的朋友可以参考下
    2015-04-04
  • C#中的in参数与性能分析详解

    C#中的in参数与性能分析详解

    这篇文章主要给大家介绍了关于C#中in参数与性能分析的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-11-11
  • C# 表达式目录树Expression的实现

    C# 表达式目录树Expression的实现

    本文主要介绍了C# 表达式目录树Expression的实现,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-09-09
  • C#设置Word文本框中改变文字方向的方法

    C#设置Word文本框中改变文字方向的方法

    在Word中可插入文本框,默认情况下插入的文本框中的文字方向为横向排列,对于一些特殊文档的设计要求,需要改变文字方向,本文就详细的介绍一下使用,感兴趣的可以了解一下
    2021-06-06
  • C#面向对象特征的具体实现及作用详解

    C#面向对象特征的具体实现及作用详解

    所有的面相对象思想,归根结底是为了简化代码,减少代码量,构建更符合现实生活逻辑的程序代码,从而减轻程序员的负担。不能一味地或者说刻意地去使用面相对象的思想而忽略了程序所实现的功能或者框架,要根据实际情况
    2013-10-10

最新评论

?


http://www.vxiaotou.com