重写TreeView模板来实现数据分层展示(一)

  总想花些时间来好好总结一下TreeView这个WPF控件,今天来通过下面的这几个例子来好好总结一下这个控件,首先来看看一个常规的带虚线的TreeView控件吧,在介绍具体如何完成之前首先来看看最终实现的效果图吧!

  然后我们来具体分析一下这个是怎样去实现的?

1 修改TreeView的模板层

其实TreeView中最重要的就是TreeViewItem项,这个决定了最终TreeView的展现方式,另外就是TreeView每展开子项时前面的ToggleButton的样式了,因为默认的TreeView样式前面是一个三角形,那么我们先从整个TreeView的样式开始说起吧!

 <Style TargetType="{x:Type TreeViewItem}">
            <Setter Property="Background" Value="Transparent"/>
            <!--此处设置TreeViewItem的绑定样式-->
            <Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}"></Setter>
            <Setter Property="HorizontalContentAlignment" Value="{Binding HorizontalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"/>
            <Setter Property="VerticalContentAlignment" Value="{Binding VerticalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"/>
            <Setter Property="Padding" Value="1,0,0,0"/>
            <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
            <Setter Property="FocusVisualStyle" Value="{StaticResource TreeViewItemFocusVisual}"/>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type TreeViewItem}">
                        <Grid>
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition MinWidth="19" Width="Auto"/>
                                <ColumnDefinition Width="150" MinWidth="150"/>
                                <ColumnDefinition />
                            </Grid.ColumnDefinitions>
                            <Grid.RowDefinitions>
                                <RowDefinition Height="Auto" MinHeight="22"/>
                                <RowDefinition  />
                            </Grid.RowDefinitions>
                            <!-- Connecting Lines -->
                            <!-- Horizontal line -->
                            <Rectangle x:Name="HorLn" Margin="9,0,0,0" Height="1" Stroke="Green" SnapsToDevicePixels="True"/>
                            <!-- Vertical line -->
                            <Rectangle x:Name="VerLn" Width="1" Stroke="Green" Margin="0,0,1,0" Grid.RowSpan="2" SnapsToDevicePixels="true" Fill="White"/>
                            <ToggleButton x:Name="Expander"
                                          Grid.Column="0"
                                          Grid.Row="0"
                                          ClickMode="Press"
                                          IsChecked="{Binding IsExpanded, RelativeSource={RelativeSource TemplatedParent}}"
                                          Style="{StaticResource ExpandCollapseToggleStyle}"/>
                            <Border x:Name="Bd" BorderBrush="{TemplateBinding BorderBrush}"
                                    BorderThickness="{TemplateBinding BorderThickness}"
                                    Background="{TemplateBinding Background}"
                                    Grid.Column="1"
                                    Padding="{TemplateBinding Padding}"
                                    SnapsToDevicePixels="true">
                                <ContentPresenter x:Name="PART_Header"
                                                  ContentSource="Header"
                                                  HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
                                                  SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
                                                  Width="250"/>
                            </Border>
                            <ItemsPresenter x:Name="ItemsHost"
                                            Grid.ColumnSpan="2"
                                            Grid.Column="1"
                                            Grid.Row="1" />
                        </Grid>
                        <ControlTemplate.Triggers>
                            <Trigger Property="IsExpanded" Value="false">
                                <Setter Property="Visibility" TargetName="ItemsHost" Value="Collapsed"/>
                            </Trigger>
                            <Trigger Property="HasItems" Value="false">
                                <Setter Property="Visibility" TargetName="Expander" Value="Hidden"/>
                            </Trigger>
                            <Trigger Property="IsSelected" Value="true">
                                <Setter Property="Background" TargetName="Bd" Value="{DynamicResource {x:Static SystemColors.HighlightBrushKey}}"/>
                                <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.HighlightTextBrushKey}}"/>
                            </Trigger>
                            <MultiTrigger>
                                <MultiTrigger.Conditions>
                                    <Condition Property="IsSelected" Value="true"/>
                                    <Condition Property="IsSelectionActive" Value="false"/>
                                </MultiTrigger.Conditions>
                                <Setter Property="Background" TargetName="Bd" Value="{DynamicResource {x:Static SystemColors.ControlBrushKey}}"/>
                                <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
                            </MultiTrigger>
                            <Trigger Property="IsEnabled" Value="false">
                                <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
                            </Trigger>
                        </ControlTemplate.Triggers>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
            <Style.Triggers>
                <Trigger Property="VirtualizingStackPanel.IsVirtualizing" Value="true">
                    <Setter Property="ItemsPanel">
                        <Setter.Value>
                            <ItemsPanelTemplate>
                                <VirtualizingStackPanel/>
                            </ItemsPanelTemplate>
                        </Setter.Value>
                    </Setter>
                </Trigger>
            </Style.Triggers>
        </Style>

  这里面重点讲述一下TreeViewItem的模板,首先TreeViewItem是有一个两行三列的Grid构成的,首先在第0行0列有两部分构成的,首先是注释部分已经写清楚了是Horizontal line 这个是每一个TreeViewItem的水平横线,实际上是一个高度为1的Rectangle,<Rectangle x:Name="HorLn" Margin="9,0,0,0" Height="1" Stroke="Green" SnapsToDevicePixels="True"/>,另外一部分是很重要的ToggleButton,这里是通过一个Style来表示的,Style="{StaticResource ExpandCollapseToggleStyle}"那么我们再具体看看这个ExpandCollapseToggleStyle的样式。

 <Style x:Key="ExpandCollapseToggleStyle" TargetType="ToggleButton">
            <Setter Property="Focusable" Value="False"/>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="ToggleButton">
                        <Grid Width="15" Height="13" SnapsToDevicePixels="True">
                            <!-- Rectangle 9x9 pixels -->
                            <Rectangle Width="15" Height="15"  Stroke="#919191" SnapsToDevicePixels="true">
                                <Rectangle.Fill>
                                    <LinearGradientBrush EndPoint="0.5,2" StartPoint="0.5,0">
                                        <GradientStop Color="#00a3d9" Offset="0"/>
                                        <GradientStop Color="#00a3d9" Offset="0.5"/>
                                        <GradientStop Color="#00a3d9" Offset="1"/>
                                    </LinearGradientBrush>
                                </Rectangle.Fill>
                            </Rectangle>
                            <!-- 画一个垂直方向的直线 -->
                            <Rectangle x:Name="ExpandPath" Width="2" Height="8" Stroke="Black" SnapsToDevicePixels="true"/>
                            <!-- 画一个水平方向的直线 -->
                            <Rectangle Width="8" Height="2" Stroke="Black" SnapsToDevicePixels="true"/>
                        </Grid>
                        <ControlTemplate.Triggers>
                            <Trigger Property="IsChecked" Value="True">
                                <Setter Property="Visibility"  TargetName="ExpandPath" Value="Collapsed"/>
                            </Trigger>
                        </ControlTemplate.Triggers>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>

  这个其实也比较简单就是在一个Grid里面放了两个Rectangle,并通过绑定IsChecked属性来改变垂直的那条直线的Visibility属性来完成最后的效果。介绍完了这一部分,我们再看看在TreeViewItem的模板中剩下的部分,在Grid的第0行第一列是用来显示每一个TreeViewItem的标题的内容,总体的结构是一个Border里面嵌套了一个ContentPresenter最终来呈现TreeViewItem的Header,这里面重点来介绍一下 ContentSource="Header" 这个属性非常重要,我们知道我们在给Header赋值时就会呈现该内容,当然赋值的内容可以是文字也可以是一个对象,如果是对象的话还需要通过写一个DataTemplate来呈现这个对象,我们来看看MSDN上的解释:获取或设置要在自动命名别名过程中使用的基名称。同时还给出了一个完整的例子,请参考下面的代码。

下面的示例演示一个用于样式 HeaderedContentControl 演示的用法 ContentSource 属性︰

<Style TargetType="HeaderedContentControl">
  <Setter Property="Template">
    <Setter.Value>
      <ControlTemplate TargetType="{x:Type HeaderedContentControl}">
        <StackPanel>
          <Grid>
            <Rectangle Stroke="{TemplateBinding Background}"/>
            <ContentPresenter ContentSource="Header"/>
          </Grid>
          <Grid>
            <Rectangle Fill="{TemplateBinding Background}"/>
            <ContentPresenter ContentSource="Content"/>
          </Grid>
        </StackPanel>
      </ControlTemplate>
    </Setter.Value>
  </Setter>
</Style>

  这里使用这个例子再合适不过了,因为TreeViewItem就是直接从HeaderedContentControl继承过来的。

在介绍了上面这些外再看看Grid中第1行第2、3两列的模板内容吧! <ItemsPresenter x:Name="ItemsHost"  Grid.ColumnSpan="2"                                   Grid.Column="1"  Grid.Row="1" /> 这个ItemsPresenter 主要是来呈现当前TreeViewItem的子项的,这个也非常重要的,否则是不会显示子项内容的,按照这样去分析整个View层的模板就都已经到位了,另外再看这些代码中两个容易被忽视的小细节,一个是FocusVisualStyle它使用的是TreeViewItemFocusVisual,这个表示当前TreeView获得焦点时呈现的状态,MSDN的解释如下:获取或设置一个属性,该属性允许自定义此元素在捕获到键盘焦点时要应用于此元素的外观、效果或其他样式特征。(继承自 FrameworkElement。)我们再来看看具体的样式代码。

  <Style x:Key="TreeViewItemFocusVisual">
            <Setter Property="Control.Template">
                <Setter.Value>
                    <ControlTemplate>
                        <Rectangle/>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
  </Style>

  另外一个重要的部分就是TreeView的Style.Triggers属性,当TreeView的 VirtualizingStackPanel.IsVirtualizing设置为True时,为ItemsPanel的模板设置为VirtualizingStackPanel,至于VirtualizingStackPanel到底有哪些作用,这些又是一个很宏大的内容,我们来看看几篇介绍得比较好的博客及文章。

2  TreeView控件绑定数据

  前面的一部分内容只是单纯去修改TreeView的模板,从而决定最终展示的形式,但是最终展示哪些内容,如何展示就涉及到另外的一部分内容即:TreeView控件的数据绑定。在我们的DEMO中,我们使用级联的数据模板来展示最终的内容,关于级联的数据模板,这里也不再赘述,直接来看代码,然后做进一步的分析。  

    <TreeView x:Name="trvMenu" Background="LightGray">
                        <TreeView.ItemTemplate>
                            <HierarchicalDataTemplate DataType="{x:Type self:MyTreeViewItem}"
                                                      ItemsSource="{Binding Children}">
                                <StackPanel Orientation="Horizontal">
                                    <Image Source="/TestTreeView;component/Images/connect.png"
                                           Width="24"
                                           Height="24"
                                           Stretch="Fill">
                                    </Image>
                                    <TextBlock Text="{Binding TreeViewItemName}" />
                                </StackPanel>
                            </HierarchicalDataTemplate>
                        </TreeView.ItemTemplate>
                    </TreeView>

  每一个TreeViewItem的最终呈现的方式是通过TreeView.ItemTemplate的模板来表现的,这里通过一个StackPanel来展示每个节点的内容,包括一个图片和一个TextBlock内容,如果有需要还可以为每个TreeViewItem前面加上CheckBox等控件,从而完成更加复杂的内容。

  最后面就是通过代码来给该TreeView绑定内容了,这里也贴出代码。这里直接在MainWindow.cs中添加相关的内容。

 private void InitDataSource()
        {
            MyTreeViewItem root = new MyTreeViewItem() {  TreeViewItemName="Root"};
            MyTreeViewItem childLevel1 = new MyTreeViewItem() { TreeViewItemName = "childLevel1" };
            MyTreeViewItem childLevel1_1 = new MyTreeViewItem() { TreeViewItemName = "childLevel1_1" };
            MyTreeViewItem childLevel1_1_1 = new MyTreeViewItem() { TreeViewItemName = "childLevel1_1_1" };
            MyTreeViewItem childLevel1_1_1_1 = new MyTreeViewItem() { TreeViewItemName = "childLevel1_1_1_1" };
            MyTreeViewItem childLevel1_1_1_2 = new MyTreeViewItem() { TreeViewItemName = "childLevel1_1_1_2" };
            MyTreeViewItem childLevel1_1_1_3 = new MyTreeViewItem() { TreeViewItemName = "childLevel1_1_1_3" };
            MyTreeViewItem childLevel1_1_1_4 = new MyTreeViewItem() { TreeViewItemName = "childLevel1_1_1_4" };
            childLevel1_1_1.Children.Add(childLevel1_1_1_1);
            childLevel1_1_1.Children.Add(childLevel1_1_1_2);
            childLevel1_1_1.Children.Add(childLevel1_1_1_3);
            childLevel1_1_1.Children.Add(childLevel1_1_1_4);
            childLevel1_1.Children.Add(childLevel1_1_1);

            MyTreeViewItem childLevel1_2 = new MyTreeViewItem() { TreeViewItemName = "childLevel1_2" };
            MyTreeViewItem childLevel1_2_1 = new MyTreeViewItem() { TreeViewItemName = "childLevel1_2_1" };
            MyTreeViewItem childLevel1_2_2 = new MyTreeViewItem() { TreeViewItemName = "childLevel1_2_2" };
            childLevel1_2.Children.Add(childLevel1_2_1);
            childLevel1_2.Children.Add(childLevel1_2_2);

            childLevel1.Children.Add(childLevel1_1);
            childLevel1.Children.Add(childLevel1_2);

            MyTreeViewItem childLevel2 = new MyTreeViewItem() { TreeViewItemName = "childLevel2" };
            MyTreeViewItem childLevel2_1 = new MyTreeViewItem() { TreeViewItemName = "childLevel2_1" };
            MyTreeViewItem childLevel2_2 = new MyTreeViewItem() { TreeViewItemName = "childLevel2_2" };
            MyTreeViewItem childLevel2_3 = new MyTreeViewItem() { TreeViewItemName = "childLevel2_3" };
            MyTreeViewItem childLevel2_4 = new MyTreeViewItem() { TreeViewItemName = "childLevel2_4" };
            childLevel2.Children.Add(childLevel2_1);
            childLevel2.Children.Add(childLevel2_2);
            childLevel2.Children.Add(childLevel2_3);
            childLevel2.Children.Add(childLevel2_4);

            root.Children.Add(childLevel1);
            root.Children.Add(childLevel2);

            trvMenu.Items.Add(root);

        }

  这里面最重要的就是MyTreeViewItem对象了,这个是绑定到TreeViewItem中的最直接的数据源,对应 DataType="{x:Type self:MyTreeViewItem}" 这里面的内容,这里也贴出相关的代码。

public class MyTreeViewItem
    {
        private string _treeViewItemName;
        public string TreeViewItemName
        {
            get { return _treeViewItemName; }
            set { _treeViewItemName = value; }
        }
        private ObservableCollection<MyTreeViewItem> _children = new ObservableCollection<MyTreeViewItem>();
        public ObservableCollection<MyTreeViewItem> Children
        {
            get
            {
                return _children;
            }
            set
            {
                if (value != _children)
                {
                    _children = value;
                }
            }

        }
    }

  这里需要说明一下,这种写法有些不规范,单由于仅仅是一个简单的DEMO,规范的写法是每一个属性都要带通知,及当前MyTreeViewItem类要实现INotifyPropertyChanged接口,并且在属性值改变时发出通知,从而通过View层去更新相应的内容。

另外需要注意的内容是 HierarchicalDataTemplate 的ItemsSource绑定的内容ItemsSource="{Binding Children}",这个Children代表的是当前节点的下一级别节点的数据源,第一级节点的数据源已经通过后台代码绑定好了,这里需要特别注意。

下一节我们将继续如何巧用TreeView的模板来实现更加多样的数据呈现方式,从而真正地去体现WPF的强大之处......

时间: 2024-10-26 10:33:15

重写TreeView模板来实现数据分层展示(一)的相关文章

重写TreeView模板来实现数据分层展示(二)

前面一片文章实现TreeView的基本的模板重写,那么照着这个思路,我们再来写一个稍稍复杂的TreeView ,其它的内容都和前面系列内容相似,还是和之前文章介绍的一样,首先看看做出的DEMO的最终样式,先来一睹为快. 其实并不复杂,关键的是要准确去理解TreeView这个控件,这个控件的核心是TreeViewItem,那么我们就重点来看看这个样式该怎么写. <Style TargetType="{x:Type TreeViewItem}"> <Setter Prop

HIVE表数据Kibana展示

如果我们想展示hive中的数据,则可以使用Kibana展示工具,而在这之前需要把hive表数据导入到es中,这就用到了ES-Hadoop插件. 插件安装: 下载地址:https://github.com/elasticsearch/elasticsearch-hadoop#readme add上面的jar包到hive hive –e "add jar elasticsearch-hadoop-2.1.1.jar;" 假如我们现在想把表dms.visit_path表中的数据展示,步骤如下

重写TreeView,多层级节点下批量显示图片,图片支持缩略图和文件名列表切换,支持调用者动态匹配选中,支持外界拖入图片并添加到对应节点下

原文:重写TreeView,多层级节点下批量显示图片,图片支持缩略图和文件名列表切换,支持调用者动态匹配选中,支持外界拖入图片并添加到对应节点下 1.先看下整体效果 2.前端代码 1 <UserControl x:Class="iPIS.UI.Base.Tree.ImageTreeControl" 2 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 3 xmlns:x=&quo

学会这一招,小白也能使用数据可视化BI软件创建医院数据实时展示大屏

灯果数据可视化BI软件是新一代人工智能数据可视化大屏软件,内置丰富的大屏模板,可视化编辑操作,无需任何经验就可以创建属于你自己的大屏.大家可以在他们的官网下载软件. 本文以医院数据实时展示大屏为例为大家演示如何在软件提供的模板基础上修改大屏. 首先我们点击我的项目页面上的新建大屏. 然后在模板中心里面选择医院数据实时展示大屏. 选中这个大屏之后,将鼠标移动到大屏上回出现一个提示按钮,提示大家是否立即使用此大屏,点击“立即使用”按钮就可以操作此大屏. 然后我们可以在这个界面上进行操作,页面上不同工

GridView等表格模板列绑定数据的方法

//绑定GridView每一行中的CheckBoxList protected void GridView1_RowDataBound(object sender, GridViewRowEventArgs e) { if (e.Row.RowType == DataControlRowType.DataRow) { CheckBoxList cbl = (CheckBoxList)e.Row.FindControl("ckbCheckBox"); if (cbl != null) {

复制一张表的数据到另一张表,jq.grid里面有时间类型数据时展示不了数据

1.复制一张表的数据到另一张表 insert into jct_sys_lock_tbl_new  (BGN_DT, END_DT, TYPE, DESCR, flag, format, range, count)  select BGN_DT, END_DT, TYPE, DESCR, flag, format, range, count    from jct_sys_lock_tbl 也可以: insert into jct_sys_lock_tbl_new  select *    fr

各种JS模板引擎对比数据(高性能JavaScript模板引擎)

最近做了JS模板引擎测试,拿各个JS模板引擎在不同浏览器上去运行同一程序,下面是模板引擎测试数据:通过测试artTemplate.juicer与doT引擎模板整体性能要有绝对优势: js模板引擎 JavaScript 模板引擎作为数据与界面分离工作中最重要一环,越来越受开发者关注,从而出现在各大型网站 Twitter.淘宝网.新浪浪微博.腾讯QQ空间.腾讯微博等.那么他们各自性能怎么样?请看下面. 通常模板引擎 baiduTemplate(百度)\artTemplate(腾讯)\juicer(淘

三维数据的展示

如果你的数据是这样的: 那么你可以考虑三维的数据如下展示 还可以这样展示 第二张图和第三张图代码如下:

基于Spring MVC的ECharts动态数据实时展示

基于SpringMVC进行ECharts动态实时数据展示,ECharts的官网示例是前端的js动态随机数据,没有与后端程序进行交互,由于本人之前对Spring MVC和ajax不太了解,所以,走了很多弯路,通过这部分的学习,让自己对MVC架构和简单的ajax有了初步的了解,下面就记录一下自己搭建的这部分程序. 首先需要配置web.xml,设置好servlet和filter,这部分可以参考众多spring示例,这里就不赘述了. 接下来就是需要将echarts的相关代码写入到一个jsp文件,代码如下