当前位置: 代码网 > it编程>编程语言>C# > WPF实现树形表格控件的示例代码

WPF实现树形表格控件的示例代码

2024年05月15日 C# 我要评论
前言本文将探讨如何利用wpf框架实现树形表格控件,该控件不仅能够有效地展示复杂的层级数据,还能够提供丰富的个性化定制选项。我们将介绍如何使用wpf提供的控件、模板、布局、数据绑定等技术来构建这样一个树

前言

本文将探讨如何利用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树形表格控件的资料请关注代码网其它相关文章!

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com