• WPF TreeView Win8 样式


    WPF 的 TreeView 控件自带的样式如图 1 的左边所示,节点前的箭头还不错,但是选中效果实在是不给力,如果想更加华丽的话,那么很有必要把 TreeView 的样式好好自定义一番。我最后得到的结果如图 1 的右边所示,是一个 Win8 风格的 TreeView。

    图 1 TreeView 样式对比

    在 WPF 中,自定义控件的外观是一件非常简单的事情,但对于 TreeView 来说,最大的困难则在于如何做到节点的整行选择。这是因为 TreeView 的项模板默认是如图 2 所示的。

    图 2 TreeView 默认的项模板

    最外层是一个三列两行的 Grid,其中第一列用于放置 Expander(节点前的箭头),剩下的列则放置 PART_Header(标头内容)和子节点列表。因为子节点列表前空出了 Expander 那一列,所以 TreeView 的每层自然就有了缩进,同时也导致绘制边框时,无法令边框填满整行——因为不知道当前节点之前有多少层缩进。虽然也有取巧的办法,例如在绘制边框时,令 Margin.Left 足够小,但这仅适用于无边框背景,在定义 Win8 样式时不可用。

    所以需要有一种办法来计算出当前节点所在的层次(即深度)才可以。这里提供了一个好办法,即通过在可视化树中沿着 TreeViewItem 的父对象向上遍历,从而得到 TreeViewItem 所在的深度,代码如下所示:

    public static int GetDepth(this TreeViewItem item) {
    	int depth = 0;
    	while ((item = item.GetAncestor<TreeViewItem>()) != null) {
    		depth++;
    	}
    	return depth;
    }
    public static T GetAncestor<T>(this DependencyObject source)
    	where T : DependencyObject {
    	do {
    		source = VisualTreeHelper.GetParent(source);
    	} while (source != null && !(source is T));
    	return source as T;
    }
    

    这里需要使用 VisualTreeHelper.GetParent 方法来得到父对象,直接用 item.Parent 得到的会是 null。

    得到了深度,接下来就将 TreeViewItem 的模板重新定义一下,如图 3 所示。

    图 3 自定义的 TreeView 项模板

    在自定义的模板中,所有节点都是使用 StackPanel 层叠排列的,所以此时是没有层次关系的,Border 是可以整行显示的。层次关系则是通过为 Grid 指定不同的 Margin.Left 来实现的,这就需要根据当前节点的深度,计算出需要缩进的距离。计算过程是利用 IValueConverter 来完成的:

    <ControlTemplate.Resources>
    	<!-- 计算节点缩进的转换器 -->
    	<cw:IndentConverter Indent="10" MarginLeft="5" x:Key="IndentConverter" />
    </ControlTemplate.Resources>
    <Grid Margin="{Binding Converter={StaticResource IndentConverter}, RelativeSource={RelativeSource TemplatedParent}}" />
    
    public sealed class IndentConverter : IValueConverter {
    	public double Indent { get; set; }
    	public double MarginLeft { get; set; }
    	public object Convert(object value, Type targetType, object parameter, CultureInfo culture) {
    		TreeViewItem item = value as TreeViewItem;
    		if (item == null) {
    			return new Thickness(0);
    		}
    		return new Thickness(this.MarginLeft + this.Indent * item.GetDepth(), 0, 0, 0);
    	}
    }
    

    这里的缩进转换器,我定义了两个属性:Indent 和 MarginLeft,这是因为我将缩进距离由 19 调整到了 12,这样会使树更好看点,但会导致根节点距离左边框过近,所以就加入了 MarginLeft,使得根节点离左边框更远些,如图 4 所示。

    图 4 缩进距离对比

    最后,就是样式的调整了。Win8 中的资源管理器树状列表的样式如图 5 所示,这个样式与 Win7 差不多,但是没有了渐变和圆角,实现起来要容易一些。需要注意的是被选中的节点,在鼠标经过的时候是会改变颜色的(虽然不是很明显),而且箭头无论是展开还是收起状态,在鼠标经过的时候都会变成蓝色。样式的定义没有什么好说的,一些颜色也在下图中标注出来了。

    图 5 Win8 中的资源管理器

    Win8 资源管理器树状列表最华丽的一点是,当控件没有焦点且鼠标未经过的时候,所有箭头是隐藏的,有焦点的时候才出现,可惜这个不知道该怎么实现,只好先放下了。

    所有的样式定义我都放在了 \Resources\Win8Theme\TreeView.xaml 文件中,这是一个资源字典,只要在需要的地方使用 <ResourceDictionary Source="Resources/Win8Theme/TreeView.xaml" /> 导入资源字典,TreeView 控件就可以以 Win8 样式显示。完整的代码和示例可以在这里下载。

  • 相关阅读:
    解决undefined reference to `__poll_chk@GLIBC_2.16' 错误
    交叉编译总结 libosscore.a libcurl.a libmysqlclient.a
    APUE环境配置
    UDT中epoll对CLOSE状态的处理
    查看ld搜索路径
    linux shell 比较文件夹内容 diff
    交互式makefile
    linux shell取文本最后一行
    linux 查看静态库,动态库是32位还是64位
    python学习day4之路
  • 原文地址:https://www.cnblogs.com/cyjb/p/WPFTreeViewWin8Theme.html
Copyright © 2020-2023  润新知