[转载][WPF]TreeViewItem实现整行选中

 

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 indentConverter},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>


 

Windows 7 Aero TreeView的效果样式,不说废话,分析样式:

        1   image


左图为Windows 7 资源管理器树型目录一部分的截图,当他放大到800%时就能看到右侧的清晰的像素级图片,可以看到音乐项的效果,放大的部分是鼠标划过树目录项的效果,简单的来分析一下效果,最外边是一个深色的单像素边框,在内侧仔细看有一层接近白色的一像素内边框,内部背景呢,则是由上而下的一个渐变颜色,用取色软件分析之后有了以下的色彩结构:

        3

开始的时候,我以为内边框只是一个简单单色边框,后来对比效果才发现,内边框原来也是由上而下的渐变颜色,TreeViewItem的控件模板结构如下:

<ControlTemplate TargetType="{x:Type TreeViewItem}">
                    <ControlTemplate.Resources>
                        <o2ds:IndentConverter Indent="19" x:Key="indentConverter" />
                    </ControlTemplate.Resources>
                    <StackPanel Height="Auto">
                        <Border x:Name="outBorder" BorderThickness="1" CornerRadius="2" Margin="1,1,0,0">
                            <Border x:Name="itemBorder" Padding="{TemplateBinding Padding}" Margin="0"                                     CornerRadius="1" Background="{TemplateBinding Background}"
                                 BorderBrush="{TemplateBinding BorderBrush}" 
                                 BorderThickness="{TemplateBinding BorderThickness}" >
                                <Grid Margin="{Binding Converter={StaticResource indentConverter},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>
                        </Border>
                        <ItemsPresenter x:Name="ItemsHost" />
                    </StackPanel>
</ControlTemplate>


在模板里面设置了根节点StackPanel,上面分析了边框是两层,所以定义了两个嵌套的Border对象,由于某些列表项可能会有不相同的设置,所以我酌情绑定了一些相对的属性,比如说在内边框绑定Padding属性,如果真有需求比如像Margin的属性可能就要酌情绑定到外侧outBorder对象上去了,至于选中效果的布局问题我在上一篇博文里面有详细的说明,具体色彩的实现以及交互效果呢,都是通过Trigger来实现的,代码如下:
 <ControlTemplate.Triggers>
    <Trigger Property="IsExpanded" Value="False">
        <Setter TargetName="ItemsHost" Property="Visibility" Value="Collapsed" />
    </Trigger>
    <Trigger Property="HasItems" Value="False">
        <Setter TargetName="ArrowButton" Property="Visibility" Value="Hidden" />
    </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>
    <Trigger Property="IsSelected"  Value="true">
        <Setter TargetName="itemBorder" Property="Background" Value="{StaticResource                 ResourceKey=SelectedBackgroundBrush}"/>
        <Setter TargetName="itemBorder" Property="BorderBrush" Value="{StaticResource                 ResourceKey=SelectedInnerBorderBrush}" />
        <Setter TargetName="outBorder" Property="BorderBrush" Value="{StaticResource                 ResourceKey=SelectedOutBorderBrush}" />
    </Trigger>
    <MultiTrigger>
        <MultiTrigger.Conditions>
            <Condition Property="IsSelected" Value="true"/>
            <Condition Property="IsSelectionActive" Value="false"/>
        </MultiTrigger.Conditions>
        <Setter TargetName="itemBorder" Property="Background" Value="{StaticResource                 ResourceKey=SelectedLostFoucsBackgroundBrush}" />
        <Setter TargetName="itemBorder" Property="BorderBrush" Value="{StaticResource                 ResourceKey=SelectedLostFoucsInnerBorderBrush}" />
        <Setter TargetName="outBorder" Property="BorderBrush" Value="{StaticResource                 ResourceKey=SelectedLostFoucsOutBorderBrush}" />
    </MultiTrigger>
    <MultiTrigger>
        <MultiTrigger.Conditions>
            <Condition Property="IsMouseOver" Value="True" />
            <Condition Property="IsSelected" Value="True" />
        </MultiTrigger.Conditions>
        <Setter TargetName="itemBorder" Property="Background" Value="{StaticResource                 ResourceKey=SelectedMouseMoveBackgroundBrush}" />
        <Setter TargetName="itemBorder" Property="BorderBrush" Value="{StaticResource                 ResourceKey=SelectedMouseMoveInnerBorderBrush}" />
        <Setter TargetName="outBorder" Property="BorderBrush" Value="{StaticResource                 ResourceKey=SelectedMouseMoveOutBorderBrush}" />
    </MultiTrigger>
    <MultiTrigger>
        <MultiTrigger.Conditions>
            <Condition SourceName="itemBorder" Property="IsMouseOver" Value="True" />
            <Condition Property="IsSelected" Value="False" />
        </MultiTrigger.Conditions>
        <Setter TargetName="itemBorder" Property="Background" Value="{StaticResource                 ResourceKey=MouseMoveBackgroundBrush}" />
        <Setter TargetName="itemBorder" Property="BorderBrush" Value="{StaticResource                 ResourceKey=MouseMoveInnerBorderBrush}" />
        <Setter TargetName="outBorder" Property="BorderBrush" Value="{StaticResource                 ResourceKey=MouseMoveOutBorderBrush}" />
    </MultiTrigger>
</ControlTemplate.Triggers>


上面代码可以看出来这里面定义了大概四种状态,分别是:

 

4

笔刷代码就不重复帖出了,状态效果配色方案如上所示。

此博文由Windows Live Writer 4 编辑完成,微软在前些天发布了Windows Live Essentials Wave 4 Beta 版本在线安装包,安装后甚是欣喜,启用了全新的Ribbon工具栏,Windows Live Mail的列表加载新添了淡入效果,而Windows Live Messanger的鼠标划入也添加了更多的动画元素,其中淡入和渐变的几个特殊效果单元与Apple新发布的iOS4操作系统的很多界面元素有重合,不知道是否最新的界面设计流行元素的前兆,待而观之。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值