WPF 自定义TreeView控件样式,仿QQ联系人列表
一、前言
TreeView控件在项目中使用比较频繁,普通的TreeView并不能满足我们的需求。因此我们需要滴对TreeView进行改造。下面的内容将介绍仿QQ联系人TreeView样式及TreeView数据绑定方法。
二、TreeView仿QQ联系人列表
准确的说不是仿QQ联系人列表,这个TreeView样式作为组织架构来使用更好。废话不多说,先看效果:

2.1、基本思路
像这种联系人列表一般涉及到多层级数据,而且有很多数据是需要动态更新的,如果通过手动一条条增加数据反而更复杂,而且不方便。因此为了绑定数据方便我们使用分层模板HierarchicalDataTemplate。
分层模板中存在两种样式,一种是分组样式,一种是人员样式。不管是分组还是人员绑定的都是对象,这样我们在对象中添加一个属性来辨别是否为分组-IsGrouping。
默认的TreeView控件四周会有边距,因此需要设置下TreeView的样式。另外鼠标经过和鼠标选中的背景色需要变化,因此还需要设置TreeViewItem的样式。
根据思路,我们需要设置三个样式,TreeView样式,TreeViewItem样式,HierarchicalDataTemplate分层模板样式。另外为了自动计算下一级的边距,我们需要添加一个转换器IndentConverter。还需要一个转换器需要将布尔类型的IsGrouping转换为Visibility,还需要一个转换器来对Visibility取反。
这样三个样式,三个转换器。就可以实现我们上面的效果,另外还可以进行动态数据绑定。
2.2、样式代码
HierarchicalDataTemplate分层模板样式代码
<HierarchicalDataTemplate x:Key="ItemNode" ItemsSource="{Binding Children,Mode=TwoWay}">
<Grid Background="Transparent">
<Grid.Resources>
<convert:BoolToVisible x:Key="boolToVisible"/>
<convert:VisibleToReverse x:Key="visibleToReverse"/>
</Grid.Resources>
<Grid MinHeight="30" x:Name="userinfo" Background="Transparent" Margin="-5 0 0 0" Visibility="{Binding Visibility,ElementName=groupinginfo,Converter={StaticResource visibleToReverse}}">
<Grid Height="50" x:Name="grid">
<Border Background="#62acf9" Width="40" Height="40" CornerRadius="4" HorizontalAlignment="Left" Margin="0 0 0 0">
<TextBlock Text="{Binding SurName}" FontSize="23" Foreground="White" VerticalAlignment="Center" HorizontalAlignment="Center"/>
</Border>
<TextBlock Text="{Binding Name}" Margin="50 7 0 0" FontSize="13"/>
<TextBlock Text="{Binding Info}" Foreground="#808080" Margin="50 30 0 0"/>
<TextBlock Text="{Binding Count,StringFormat={}{0}人}" Foreground="#808080" HorizontalAlignment="Right" VerticalAlignment="Center" Margin="0 0 5 0"/>
</Grid>
</Grid>
<StackPanel MinHeight="25" x:Name="groupinginfo" Orientation="Horizontal" Background="Transparent" HorizontalAlignment="Left" Visibility="{Binding IsGrouping,Converter={StaticResource boolToVisible}}">
<TextBlock Text="{Binding DisplayName}" Margin="3 0" VerticalAlignment="Center" HorizontalAlignment="Left"/>
</StackPanel>
</Grid>
</HierarchicalDataTemplate>
TreeViewItem样式代码
<Style x:Key="DefaultTreeViewItem" TargetType="{x:Type TreeViewItem}">
<Setter Property="MinHeight" Value="25" />
<Setter Property="Background" Value="Transparent" />
<Setter Property="SnapsToDevicePixels" Value="True" />
<Setter Property="Margin" Value="0" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TreeViewItem}">
<ControlTemplate.Resources>
<convert:IndentConverter x:Key="indentConverter"/>
</ControlTemplate.Resources>
<Grid Background="Transparent">
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Border Name="itemBackground" Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Padding="{TemplateBinding Padding}">
<Grid Background="Transparent">
<Grid x:Name="ItemRoot" Margin="{Binding Converter={StaticResource indentConverter},RelativeSource={RelativeSource TemplatedParent}}" Background="Transparent">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="16" />
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<ToggleButton x:Name="Expander" HorizontalAlignment="Left" ClickMode="Press" IsChecked="{Binding IsExpanded, RelativeSource={RelativeSource TemplatedParent}}">
<ToggleButton.Style>
<Style TargetType="{x:Type ToggleButton}">
<Setter Property="Focusable" Value="False"/>
<Setter Property="Width" Value="16"/>
<Setter Property="Height" Value="16"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ToggleButton}">
<Border Background="Transparent" Height="16" Padding="5" Width="16">
<Path x:Name="ExpandPath" Data="M0,0 L0,6 L6,0 z" Fill="#66645e" Stroke="#66645e">
<Path.RenderTransform>
<RotateTransform Angle="135" CenterY="3" CenterX="3"/>
</Path.RenderTransform>
</Path>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsChecked" Value="True">
<Setter Property="RenderTransform" TargetName="ExpandPath">
<Setter.Value>
<RotateTransform Angle="180" CenterY="3" CenterX="3"/>
</Setter.Value>
</Setter>
<Setter Property="Fill" TargetName="ExpandPath" Value="#66645e"/>
<Setter Property="Stroke" TargetName="ExpandPath" Value="#66645e"/>
</Trigger>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Stroke" TargetName="ExpandPath" Value="#66645e"/>
<Setter Property="Fill" TargetName="ExpandPath" Value="#66645e"/>
</Trigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsMouseOver" Value="True"/>
<Condition Property="IsChecked" Value="True"/>
</MultiTrigger.Conditions>
<Setter Property="Stroke" TargetName="ExpandPath" Value="#66645e"/>
<Setter Property="Fill" TargetName="ExpandPath" Value="#66645e"/>
</MultiTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ToggleButton.Style>
</ToggleButton>
<ContentPresenter Grid.Column="1" x:Name="PART_Header" ContentSource="Header"
HorizontalAlignment="Stretch" >
</ContentPresenter>
</Grid>
</Grid>
</Border>
<ItemsPresenter x:Name="ItemsHost" Grid.Row="1"/>
</Grid>
<ControlTemplate.Triggers>
<DataTrigger Binding="{Binding IsGrouping}" Value="false">
<Setter Property="Visibility" TargetName="Expander" Value="Hidden"/>
</DataTrigger>
<Trigger Property="HasItems" Value="False">
<Setter Property="Visibility" TargetName="Expander" Value="Collapsed"/>
</Trigger>
<Trigger Property="IsExpanded" Value="False">
<Setter Property="Visibility" TargetName="ItemsHost" Value="Collapsed"/>
</Trigger>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Background" TargetName="itemBackground" Value="#FAE388"/>
</Trigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsFocused" Value="False"/>
<Condition SourceName="itemBackground" Property="IsMouseOver" Value="true"/>
</MultiTrigger.Conditions>
<Setter Property="Background" Value=" #fceeb9" TargetName="itemBackground"/>
</MultiTrigger>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
TreeView样式代码
<Style x:Key="DefaultTreeView" TargetType="{x:Type TreeView}">
<Setter Property="ScrollViewer.CanContentScroll" Value="True" />
<Setter Property="VirtualizingStackPanel.IsVirtualizing" Value="True"></Setter>
<Setter Property="VirtualizingStackPanel.VirtualizationMode" Value="Recycling" />
<Setter Property="ScrollViewer.IsDeferredScrollingEnabled" Value="False" />
<Setter Property="ItemContainerStyle" Value="{StaticResource DefaultTreeViewItem}"></Setter>
<Setter Property="Padding" Value="0"/>
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<VirtualizingStackPanel IsItemsHost="True" Margin="0"/>
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
</Style>
2.3、转换器代码
public class IndentConverter : IValueConverter
{ public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
double colunwidth = ;
double left = 0.0; UIElement element = value as TreeViewItem;
while (element.GetType() != typeof(TreeView))
{
element = (UIElement)VisualTreeHelper.GetParent(element);
if (element.GetType() == typeof(TreeViewItem))
left += colunwidth;
}
return new Thickness(left, , , );
} public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
} } public class BoolToVisible : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if ((bool)value)
return Visibility.Visible;
else
return Visibility.Collapsed;
} public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
} public class VisibleToReverse : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if ((Visibility)value == Visibility.Visible)
return Visibility.Collapsed;
else
return Visibility.Visible;
} public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
2.4、引用示例
<TreeView x:Name="TreeViewOrg" BorderThickness="1" BorderBrush="#BBB" Background="Transparent" Width="280" Height="500" Margin="10" ItemTemplate="{StaticResource ItemNode}" Style="{StaticResource DefaultTreeView}">
</TreeView>
2.5、初始化数据源及绑定对象
public MainWindow()
{
InitializeComponent();
OrgList = new ObservableCollection<OrgModel>()
{
new OrgModel()
{
IsGrouping=true,
DisplayName="单位名称(3/7)",
Children=new ObservableCollection<OrgModel>()
{
new OrgModel(){
IsGrouping=true,
DisplayName="未分组联系人(2/4)",
Children=new ObservableCollection<OrgModel>()
{
new OrgModel(){
IsGrouping=false,
SurName="刘",
Name="刘棒",
Info="我要走向天空!",
Count=
}
}
}
},
} };
TreeViewOrg.ItemsSource = OrgList;
} public ObservableCollection<OrgModel> OrgList { get; set; } public class OrgModel
{
public bool IsGrouping { get; set; }
public ObservableCollection<OrgModel> Children { get; set; }
public string DisplayName { get; set; }
public string SurName { get; set; }
public string Name { get; set; }
public string Info { get; set; }
public int Count { get; set; }
}
所有代码已经上传到github:https://github.com/cmfGit/WpfDemo.git
WPF 自定义TreeView控件样式,仿QQ联系人列表的更多相关文章
- WPF自定义分页控件,样式自定义,简单易用
WPF自定义分页控件 做了许久伸手党,终于有机会贡献一波,搜索一下WPF分页控件,还是多,但是不太通用,主要就是样式问题,这个WPF很好解决,还有一个就是分页控件嘛,只关心几个数字的变动就行了,把页码 ...
- WPF 自定义TabControl控件样式
一.前言 程序中经常会用到TabControl控件,默认的控件样式很普通.而且样式或功能不一定符合我们的要求.比如:我们需要TabControl的标题能够居中.或平均分布:或者我们希望TabContr ...
- wpf 自定义RadioButton控件样式
实现的效果为: 我感觉来自定义RadioButton样式和定义button空间的样式差不多,只是类型不同而已. 接下来分析一下样式代码: <!--自定义单选按钮样式--> & ...
- WPF 自定义DataGrid控件样式
内容转自https://www.cnblogs.com/xiaogangqq123/archive/2012/05/07/2487166.html 一.DataGrid基本样式(一) 小刚已经把Dat ...
- 动手分析安卓仿QQ联系人列表TreeView控件
因项目需要需要用到仿QQ联系人列表的控件样式,于是网上找到一个轮子(https://github.com/TealerProg/TreeView),工作完成现在简单分析一下这个源码. 一. 需要用 ...
- WPF Calendar 日历控件 样式自定义
原文:WPF Calendar 日历控件 样式自定义 粗略的在代码上做了些注释 blend 生成出来的模版 有的时候 会生成 跟 vs ui界面不兼容的代码 会导致可视化设计界面 报错崩溃掉 但是确不 ...
- arcgis api for js共享干货系列之二自定义Navigation控件样式风格
arcgis api for js默认的Navigation控件样式风格如下图: 这样的风格不能说不好,各有各的爱好,审美观,这里也不是重点,这里的重点是如何自定义一套自己喜欢的样式风格呢:自己自定义 ...
- arcgis api 3.x for js 共享干货系列之二自定义 Navigation 控件样式风格(附源码下载)
0.内容概览 自定义 Navigation 控件样式风格 源码下载 1.内容讲解 arcgis api 3.x for js 默认的Navigation控件样式风格如下图:这样的风格不能说不好,各有各 ...
- WPF设置全局控件样式
原文:WPF设置全局控件样式 方法: 在资源文件APP.XAML中添加如下资源 <Application x:Class="_360UI.App" xmlns="h ...
随机推荐
- 网络通信 --> socket通信
socket通信 socket是应用层与TCP/IP协议族通信的中间软件抽象层,是一组接口.工作原理如下: 具体过程:服务器端先初始化socket,然后与端口绑定(bind),对端口进行监听(list ...
- Linux下的进程与线程(二)—— 信号
Linux进程之间的通信: 本文主要讨论信号问题. 在Linux下的进程与线程(一)中提到,调度器可以用中断的方式调度进程. 然而,进程是怎么知道自己需要被调度了呢?是内核通过向进程发送信号,进程才得 ...
- 移动端H5地图矢量SHP网格切分打包方案
文章版权由作者李晓晖和博客园共有,若转载请于明显处标明出处:http://www.cnblogs.com/naaoveGIS/ 1.背景 与离线瓦片方案一样,同样是为了解决移动端网速和流量问题,但是却 ...
- drbd(四):drbd多节点(drbd9)
1.drbd多节点简介 在drbd9以前,drbd一直只能配置两个节点,要么是primary/secondary,要么是primary/primary.虽然在这些版本上也能配置第三个节点实现三路节点的 ...
- @Cacheable的实现原理
如果你用过Spring Cache,你一定对这种配置和代码不陌生: <cache:annotation-driven cache-manager="cacheManager" ...
- alpha冲刺第二天
一.合照 二.项目燃尽图 三.项目进展 图形界面基本完成 接口文档框架完成,接下来将会不断细化填充 登录界面向服务器请求数据进行ing 四.明日规划 1.注册登录接口能够完成 2.研究idea实现获得 ...
- Linux下C编写基本的多线程socket服务器
不想多说什么,会搜这些东西的都是想看代码的吧. 一开始不熟悉多线程的时候还在想怎么来控制一个线程的结束,后来发现原来有pthread_exit()函数可以直接在线程函数内部调用结束这个线程. 开始还想 ...
- 团队作业8——测试与发布(Beta阶段)
Deadline: 2017-12-17 23:00PM,以博客发表日期为准. 评分基准: 按时交 - 有分,检查的项目包括后文的三个方面 测试报告 发布说明 展示博客(单独一篇博客) 晚交 - ...
- verilog学习笔记(1)_两个小module
第一个小module-ex_module module ex_module( input wire sclk,//声明模块的时候input变量一定是wire变量 input wire rst_n,// ...
- Django Haystack 全文检索与关键词高亮
Django Haystack 简介 django-haystack 是一个专门提供搜索功能的 django 第三方应用,它支持 Solr.Elasticsearch.Whoosh.Xapian 等多 ...