现在的位置: 首页 > 综合 > 正文

No.8948 实现Windows 7样式Aero TreeView控件(二):实现整行选中

2013年04月06日 ⁄ 综合 ⁄ 共 4192字 ⁄ 字号 评论关闭

full selector

记得原来做Winfrom通过Item的Bounds可以获得整行的区域,但是在WPF中进行了几个布局方式都没能成功!
VS中的解决方案效果布局如下:

wpfTreeStyle

这样在第一行第二列方式Border控件,此为选中区域。而如果要实现右侧系统的效果,必须要在选中区域的时候补齐第二行第一列的宽度,但是如果在TreeViewItem的Template不设置子节点列表缩进的话,将无法定位子节点列表缩进!
对比了Winform的TreeNode类型有两个关键的属性:FullPath和Level。只要知道一个就可以根据Indent算出相应的缩进宽度,从这样的思路上,这个Item布局结构就要更改如下了:
aeroTreeStyle 
代码结构如下:

<StackPanel>

    <Border x:Name=”itemBorder”>

           <Grid>

                  <Grid.ColumnDefinitions>

                        <ColumnDefinitions Width=”19” />

                        <ColumnDefinitions Width=”*” />

                  </Grid.ColumnDefinitions>

                  <Path x:Name=”TreeArrow” Grid.Column=”0” />

                  <ContentPresenter ContentSource=”Header” Grid.Column=”1”  />

           </Grid>

   </Border>

   <ItemsPresenter x:Name="ItemsHost" />

</StackPanel>

可是此时的子节点列表没有缩进怎么办?
做几个预备动作,在TreeViewItem里面有一个TreeNode的特殊特性,叫做Level,获得节点所在的层级,在这里我做了个Extensions类:

public static class TreeViewItemExtensions
   {
       public static int GetDepth(this TreeViewItem item)
       {
           FrameworkElement elem = item;
           while (elem.Parent != null)
           {
               var tvi = elem.Parent as TreeViewItem;
               if (null != tvi)
                   return tvi.GetDepth() + 1;
               elem = elem.Parent as FrameworkElement;
           }
           return 0;
       }
   }

用来获得TreeViewItem在TreeView里面的层级深度。然后就是怎么将缩进的绑定到它该在的地方了!

在上面那段TreeViewItem的Template代码里面的itemBorder就是节点项的整体范围了,如果我们想让选中边框始终保持Aero样式中满行选中的状态就只能在itemBorder中的Grid上做手脚了,上面扩展了TreeViewItem对象,可以获取的到层级深度,而缩进值则等于节点层级和缩进值的乘积,而应用缩进值需要实现一个缩进的转换类型方法:

public class IndentConverter:IValueConverter
    {
        public double Indent{ get; set; }

        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            var item = value as TreeViewItem;
            if (null == item)
                return new Thickness(0);
            return new Thickness(Indent* item.GetDepth(), 0, 0, 0);
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }

然后就是改造上一段模板内容。完整代码如下:

<Style TargetType="{x:Type TreeViewItem}" x:Key="aaa">
       <Setter Property="FocusVisualStyle" Value="{StaticResource TreeViewItemFocusVisual}"/>
       <Setter Property="Template">
           <Setter.Value>
               <ControlTemplate TargetType="{x:Type TreeViewItem}">
                   <ControlTemplate.Resources>
                      <o2ds:IndentConverter Indent="19" x:Key="indentConverter" />
                   </ControlTemplate.Resources>
                   <StackPanel>
                       <Border Name="itemBackground" Background="{TemplateBinding Background}"
                                BorderBrush="{TemplateBinding BorderBrush}"
                                BorderThickness="{TemplateBinding BorderThickness}"
                                Padding="{TemplateBinding Padding}">
                           <Grid Margin="{Binding Converter={StaticResource lengthConverter},RelativeSource={RelativeSource TemplatedParent}}">
                               <Grid.ColumnDefinitions>
                                   <ColumnDefinition Width="19" />
                                   <ColumnDefinition />
                               </Grid.ColumnDefinitions>
                               <ToggleButton Grid.Column="0" x:Name="ArrowButton" Style="{StaticResource TreeViewArrowButtonStyle}"
                                             IsChecked="{Binding Path=IsExpanded, RelativeSource={RelativeSource TemplatedParent}}"
                                             ClickMode="Press" />
                               <ContentPresenter Grid.Column="1" x:Name="PART_Header" ContentSource="Header"
                                                 HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" />
                           </Grid>
                       </Border>
                       <ItemsPresenter x:Name="ItemsHost" />
                   </StackPanel>

                <ControlTemplate.Triggers>

                  Trigger something…
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

 

 

<Grid Margin="{Binding Converter={StaticResource indentConverter},RelativeSource={RelativeSource TemplatedParent}}">

indentConverter对象承担了获取深度以及转换边距的任务。由此我们就完成了实现整行选中TreeViewItem的任务,至于Aero样式的效果细节,后面放出。

抱歉!评论已关闭.