WPF自定义TreeView控件样式实现QQ联系人列表效果
一、前言
treeview这个控件对于我来说是用得比较多的,以前做的小聊天软件(好友列表)、音乐播放器(播放列表)、类库展示器(树形类结构)等都用的是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 = 10; 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, 0, 0, 0); } 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=3 } } } }, } }; 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; } }
总结
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对的支持。