前言
本文将探讨如何利用wpf框架实现树形表格控件,该控件不仅能够有效地展示复杂的层级数据,还能够提供丰富的个性化定制选项。我们将介绍如何使用wpf提供的控件、模板、布局、数据绑定等技术来构建这样一个树形表格。
一、运行效果
1.1默认样式
1.2 自定义样式
二、代码实现
2.1 创建自定义控件(treelistview)
新建一个继承自treeview的控件,并定义一个类型为viewbase的view依赖属性,用于在代码中指定列。
public class treelistview : treeview { public viewbase view { get { return (viewbase)getvalue(viewproperty); } set { setvalue(viewproperty, value); } } public static readonly dependencyproperty viewproperty = dependencyproperty.register("view", typeof(viewbase), typeof(treelistview)); }
2.2 在treelistview控件模板中处理列头
为了在treelistview中显示列头,需要在合适的位置添加gridviewheaderrowpresenter控件,并在columns属性上绑定我们之前定义的view.columns属性。下面我们首先来分析treeview控件模板的代码。
<controltemplate targettype="{x:type treeview}"> <border x:name="bd" borderbrush="{templatebinding borderbrush}" borderthickness="{templatebinding borderthickness}" snapstodevicepixels="true"> <scrollviewer x:name="_tv_scrollviewer_" background="{templatebinding background}" cancontentscroll="false" focusable="false" horizontalscrollbarvisibility="{templatebinding scrollviewer.horizontalscrollbarvisibility}" padding="{templatebinding padding}" snapstodevicepixels="{templatebinding snapstodevicepixels}" verticalscrollbarvisibility="{templatebinding scrollviewer.verticalscrollbarvisibility}"> <itemspresenter/> </scrollviewer> </border> <controltemplate.triggers> <trigger property="isenabled" value="false"> <setter property="background" targetname="bd" value="{dynamicresource {x:static systemcolors.controlbrushkey}}"/> </trigger> <trigger property="virtualizingpanel.isvirtualizing" value="true"> <setter property="cancontentscroll" targetname="_tv_scrollviewer_" value="true"/> </trigger> </controltemplate.triggers> </controltemplate>
通过以上代码我们可以看出,只要将gridviewheaderrowpresenter控件添加到scrollviewer控件上面即可实现列头功能,但这样会有一个问题,那就是内容宽度超出控件宽度后,鼠标拖动横向滚动条时列头不会跟随下方的数据列表一起滚动。为解决这个问题我们需要将gridviewheaderrowpresenter放置到scrollviewer控件模板中,以下为完整代码。
<style x:key="{x:static gridview.gridviewscrollviewerstylekey}" targettype="{x:type scrollviewer}"> <setter property="focusable" value="false" /> <setter property="template"> <setter.value> <controltemplate targettype="{x:type scrollviewer}"> <grid background="{templatebinding background}" snapstodevicepixels="true"> <grid.columndefinitions> <columndefinition width="*" /> <columndefinition width="auto" /> </grid.columndefinitions> <grid.rowdefinitions> <rowdefinition height="*" /> <rowdefinition height="auto" /> </grid.rowdefinitions> <dockpanel margin="{templatebinding padding}"> <scrollviewer dockpanel.dock="top" focusable="false" horizontalscrollbarvisibility="hidden" verticalscrollbarvisibility="hidden"> <gridviewheaderrowpresenter margin="2,0,2,0" allowscolumnreorder="{binding templatedparent.view.allowscolumnreorder, relativesource={relativesource templatedparent}}" columnheadercontainerstyle="{binding templatedparent.view.columnheadercontainerstyle, relativesource={relativesource templatedparent}}" columnheadercontextmenu="{binding templatedparent.view.columnheadercontextmenu, relativesource={relativesource templatedparent}}" columnheaderstringformat="{binding templatedparent.view.columnheaderstringformat, relativesource={relativesource templatedparent}}" columnheadertemplate="{binding templatedparent.view.columnheadertemplate, relativesource={relativesource templatedparent}}" columnheadertemplateselector="{binding templatedparent.view.columnheadertemplateselector, relativesource={relativesource templatedparent}}" columnheadertooltip="{binding templatedparent.view.columnheadertooltip, relativesource={relativesource templatedparent}}" columns="{binding templatedparent.view.columns, relativesource={relativesource templatedparent}}" snapstodevicepixels="{templatebinding snapstodevicepixels}" /> </scrollviewer> <scrollcontentpresenter x:name="part_scrollcontentpresenter" cancontentscroll="{templatebinding cancontentscroll}" content="{templatebinding content}" contenttemplate="{templatebinding contenttemplate}" keyboardnavigation.directionalnavigation="local" snapstodevicepixels="{templatebinding snapstodevicepixels}" /> </dockpanel> <scrollbar x:name="part_horizontalscrollbar" grid.row="1" cursor="arrow" maximum="{templatebinding scrollablewidth}" minimum="0.0" orientation="horizontal" viewportsize="{templatebinding viewportwidth}" visibility="{templatebinding computedhorizontalscrollbarvisibility}" value="{binding horizontaloffset, mode=oneway, relativesource={relativesource templatedparent}}" /> <scrollbar x:name="part_verticalscrollbar" grid.column="1" cursor="arrow" maximum="{templatebinding scrollableheight}" minimum="0.0" orientation="vertical" viewportsize="{templatebinding viewportheight}" visibility="{templatebinding computedverticalscrollbarvisibility}" value="{binding verticaloffset, mode=oneway, relativesource={relativesource templatedparent}}" /> <dockpanel grid.row="1" grid.column="1" background="{binding background, elementname=part_verticalscrollbar}" lastchildfill="false"> <rectangle width="1" dockpanel.dock="left" fill="white" visibility="{templatebinding computedverticalscrollbarvisibility}" /> <rectangle height="1" dockpanel.dock="top" fill="white" visibility="{templatebinding computedhorizontalscrollbarvisibility}" /> </dockpanel> </grid> </controltemplate> </setter.value> </setter> </style> <style targettype="{x:type local:treelistview}"> <setter property="scrollviewer.horizontalscrollbarvisibility" value="auto" /> <setter property="scrollviewer.verticalscrollbarvisibility" value="auto" /> <setter property="scrollviewer.cancontentscroll" value="true" /> <setter property="template"> <setter.value> <controltemplate targettype="{x:type local:treelistview}"> <border borderbrush="{templatebinding borderbrush}" borderthickness="{templatebinding borderthickness}"> <scrollviewer padding="{templatebinding padding}" style="{staticresource {x:static gridview.gridviewscrollviewerstylekey}}"> <itemspresenter /> </scrollviewer> </border> </controltemplate> </setter.value> </setter> </style>
2.3 在treelistviewitem模板中处理子项的展开和收缩
新建一个继承自treeviewitem的类,命名为treelistviewitem(如有个性化需求,可以在该类中处理),编辑控件模板,在模板中添加以下代码。
<style targettype="{x:type local:treelistviewitem}"> <setter property="borderthickness" value="1" /> <setter property="template"> <setter.value> <controltemplate targettype="{x:type local:treelistviewitem}"> <stackpanel> <border name="bd" padding="{templatebinding padding}" background="{templatebinding background}" borderbrush="{templatebinding borderbrush}" borderthickness="{templatebinding borderthickness}"> <gridviewrowpresenter x:name="part_header" columns="{binding relativesource={relativesource ancestortype=local:treelistview}, path=view.columns}" content="{templatebinding header}" /> </border> <itemspresenter x:name="itemshost" /> </stackpanel> <controltemplate.triggers> <trigger property="isexpanded" value="false"> <setter targetname="itemshost" property="visibility" value="collapsed" /> </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> <multitrigger> <multitrigger.conditions> <condition property="extensions:treeviewitemextensions.ismousedirectlyoveritem" value="true" /> </multitrigger.conditions> <setter targetname="bd" property="background" value="{staticresource item.mouseover.background}" /> <setter targetname="bd" property="borderbrush" value="{staticresource item.mouseover.border}" /> </multitrigger> <multitrigger> <multitrigger.conditions> <condition property="selector.isselectionactive" value="false" /> <condition property="isselected" value="true" /> </multitrigger.conditions> <setter targetname="bd" property="background" value="{staticresource item.selectedinactive.background}" /> <setter targetname="bd" property="borderbrush" value="{staticresource item.selectedinactive.border}" /> </multitrigger> <multitrigger> <multitrigger.conditions> <condition property="selector.isselectionactive" value="true" /> <condition property="isselected" value="true" /> </multitrigger.conditions> <setter targetname="bd" property="background" value="{staticresource item.selectedactive.background}" /> <setter targetname="bd" property="borderbrush" value="{staticresource item.selectedactive.border}" /> </multitrigger> <trigger property="isenabled" value="false"> <setter property="foreground" value="{dynamicresource {x:static systemcolors.graytextbrushkey}}" /> </trigger> </controltemplate.triggers> </controltemplate> </setter.value> </setter> </style>
此处的核心在于模板中添加了gridviewrowpresenter控件,并在columns属性上绑定了我们之前定义的view.columns属性,这样就可以在每一行上面显示列数据。还有一个关键点是itemspresenter,它用于显示子项数据,此处命名为itemshost,它由属性触发器中的代码来控件展开和收起。以下是属性触发器代码。
<trigger property="isexpanded" value="false"> <setter targetname="itemshost" property="visibility" value="collapsed" /> </trigger>
2.4 在单元格模板中控件子项的展开与收起
为了达到展开和收起的效果,需要在首列的单元格中控制treelistviewitem的isexpanded属性。以下为完整代码。
<datatemplate x:key="expandcelltemplate"> <dockpanel> <togglebutton x:name="expander" margin="{binding path=level, converter={staticresource levelindentconverter}, relativesource={relativesource ancestortype={x:type treelistviewitem}}}" clickmode="press" ischecked="{binding path=isexpanded, relativesource={relativesource ancestortype={x:type treelistviewitem}}}" style="{staticresource expandcollapsetogglestyle}" /> <textblock text="{binding property1}" /> </dockpanel> <datatemplate.triggers> <datatrigger binding="{binding path=hasitems, relativesource={relativesource ancestortype={x:type treelistviewitem}}}" value="false"> <setter targetname="expander" property="visibility" value="hidden" /> </datatrigger> </datatemplate.triggers> </datatemplate>
其关键代码为
ischecked="{binding path=isexpanded, relativesource={relativesource ancestortype={x:type treelistviewitem}}}"
2.5 控件使用
<treelistview itemssource="{binding collection}"> <treelistview.itemtemplate> <hierarchicaldatatemplate itemssource="{binding collection, isasync=true}" /> </treelistview.itemtemplate> <treelistview.view> <gridview> <gridviewcolumn celltemplate="{staticresource expandcelltemplate}" header="property1" /> <gridviewcolumn displaymemberbinding="{binding property2}" header="property2" /> <gridviewcolumn displaymemberbinding="{binding property3}" header="property3" /> <gridviewcolumn displaymemberbinding="{binding property4}" header="property4" /> <gridviewcolumn displaymemberbinding="{binding property5}" header="property5" /> <gridviewcolumn displaymemberbinding="{binding property6}" header="property6" /> <gridviewcolumn displaymemberbinding="{binding property7}" header="property7" /> <gridviewcolumn displaymemberbinding="{binding property8}" header="property8" /> <gridviewcolumn displaymemberbinding="{binding property9}" header="property9" /> <gridviewcolumn displaymemberbinding="{binding property10}" header="property10" /> <gridviewcolumn displaymemberbinding="{binding property11}" header="property11" /> <gridviewcolumn displaymemberbinding="{binding property12}" header="property12" /> </gridview> </treelistview.view> </treelistview>
以上就是wpf实现树形表格控件的示例代码的详细内容,更多关于wpf树形表格控件的资料请关注代码网其它相关文章!
发表评论