欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

WPF中Expander的用法和控件模板详解

程序员文章站 2023-11-14 17:07:58
一、Expander的用法 在WPF中,Expander是一个很实用的复合控件,可以很方便的实现下拉菜单和导航栏等功能。先介绍简单的用法,而后分析他的控件模板。 可以看到Expander主要分为头部和内容两部分,展开时才显示内容,而内容部分可以存放丰富的内容 效果图: 二、控件模板 如何获取控件本身 ......

一、expander的用法

  在wpf中,expander是一个很实用的复合控件,可以很方便的实现下拉菜单和导航栏等功能。先介绍简单的用法,而后分析他的控件模板。

<window.resources>
        <resourcedictionary>
            <style x:key="expander.stackpanel.style" targettype="frameworkelement">
                <setter property="maxwidth" value="80"></setter>
                <setter property="minwidth" value="50"></setter>
                <setter property="horizontalalignment" value="left"></setter>
            </style>
        </resourcedictionary>
    </window.resources>
    <grid>
        
        <expander style="{dynamicresource expanderstyle}" header="头部">
            <stackpanel>
                <button style="{staticresource expander.stackpanel.style}">内容1</button>
                <radiobutton style="{staticresource expander.stackpanel.style}">内容2</radiobutton>
                <textbox style="{staticresource expander.stackpanel.style}">内容3</textbox>
                <textblock style="{staticresource expander.stackpanel.style}">内容4</textblock>
            </stackpanel>
        </expander>
    </grid>

可以看到expander主要分为头部和内容两部分,展开时才显示内容,而内容部分可以存放丰富的内容

效果图:

WPF中Expander的用法和控件模板详解

二、控件模板

  如何获取控件本身默认的控件模板请看我的另一篇文章:

  因为控件模板比较复杂,先看大概的构成:

WPF中Expander的用法和控件模板详解

 

再看看主样式部分(expandersytle)的构成:

WPF中Expander的用法和控件模板详解

 

再看向下展开时头部的样式构成:

WPF中Expander的用法和控件模板详解

 

如下为我标注了注释的完整控件模板代码:

<solidcolorbrush x:key="expander.mouseover.circle.stroke" color="#ff5593ff"/>
            <solidcolorbrush x:key="expander.mouseover.circle.fill" color="#fff3f9ff"/>
            <solidcolorbrush x:key="expander.mouseover.arrow.stroke" color="#ff000000"/>
            <solidcolorbrush x:key="expander.pressed.circle.stroke" color="#ff3c77dd"/>
            <solidcolorbrush x:key="expander.pressed.circle.fill" color="#ffd9ecff"/>
            <solidcolorbrush x:key="expander.pressed.arrow.stroke" color="#ff000000"/>
            <solidcolorbrush x:key="expander.disabled.circle.stroke" color="#ffbcbcbc"/>
            <solidcolorbrush x:key="expander.disabled.circle.fill" color="#ffe6e6e6"/>
            <solidcolorbrush x:key="expander.disabled.arrow.stroke" color="#ff707070"/>
            <solidcolorbrush x:key="expander.static.circle.fill" color="#ffffffff"/>
            <solidcolorbrush x:key="expander.static.circle.stroke" color="#ff333333"/>
            <solidcolorbrush x:key="expander.static.arrow.stroke" color="#ff333333"/>
            <!--expanddirection=right,即向右展开时的头部样式-->
            <style x:key="expanderrightheaderstyle" targettype="{x:type togglebutton}">
                <setter property="template">
                    <setter.value>
                        <controltemplate targettype="{x:type togglebutton}">
                            <border padding="{templatebinding padding}">
                                <grid background="transparent" snapstodevicepixels="false">
                                    <grid.rowdefinitions>
                                        <rowdefinition height="19"/>
                                        <rowdefinition height="*"/>
                                    </grid.rowdefinitions>
                                    <grid>
                                        <grid.layouttransform>
                                            <transformgroup>
                                                <transformgroup.children>
                                                    <transformcollection>
                                                        <rotatetransform angle="-90"/>
                                                    </transformcollection>
                                                </transformgroup.children>
                                            </transformgroup>
                                        </grid.layouttransform>
                                        <ellipse x:name="circle" fill="yellow" horizontalalignment="center" height="19" stroke="yellow" verticalalignment="center" width="19"/>
                                        <path x:name="arrow" data="m 1,1.5 l 4.5,5 l 8,1.5" horizontalalignment="center" snapstodevicepixels="false" stroke="{staticresource expander.static.arrow.stroke}" strokethickness="2" verticalalignment="center"/>
                                    </grid>
                                    <contentpresenter horizontalalignment="center" margin="0,4,0,0" grid.row="1" recognizesaccesskey="true" snapstodevicepixels="true" verticalalignment="top"/>
                                </grid>
                            </border>
                            <controltemplate.triggers>
                                <trigger property="ischecked" value="true">
                                    <setter property="data" targetname="arrow" value="m 1,4.5  l 4.5,1  l 8,4.5"/>
                                </trigger>
                                <trigger property="ismouseover" value="true">
                                    <setter property="stroke" targetname="circle" value="{staticresource expander.mouseover.circle.stroke}"/>
                                    <setter property="fill" targetname="circle" value="{staticresource expander.mouseover.circle.fill}"/>
                                    <setter property="stroke" targetname="arrow" value="{staticresource expander.mouseover.arrow.stroke}"/>
                                </trigger>
                                <trigger property="ispressed" value="true">
                                    <setter property="stroke" targetname="circle" value="{staticresource expander.pressed.circle.stroke}"/>
                                    <setter property="strokethickness" targetname="circle" value="1.5"/>
                                    <setter property="fill" targetname="circle" value="{staticresource expander.pressed.circle.fill}"/>
                                    <setter property="stroke" targetname="arrow" value="{staticresource expander.pressed.arrow.stroke}"/>
                                </trigger>
                                <trigger property="isenabled" value="false">
                                    <setter property="stroke" targetname="circle" value="{staticresource expander.disabled.circle.stroke}"/>
                                    <setter property="fill" targetname="circle" value="{staticresource expander.disabled.circle.fill}"/>
                                    <setter property="stroke" targetname="arrow" value="{staticresource expander.disabled.arrow.stroke}"/>
                                </trigger>
                            </controltemplate.triggers>
                        </controltemplate>
                    </setter.value>
                </setter>
            </style>
            <!--expanddirection=up,即向上展开时的头部样式-->
            <style x:key="expanderupheaderstyle" targettype="{x:type togglebutton}">
                <setter property="template">
                    <setter.value>
                        <controltemplate targettype="{x:type togglebutton}">
                            <border padding="{templatebinding padding}">
                                <grid background="transparent" snapstodevicepixels="false">
                                    <grid.columndefinitions>
                                        <columndefinition width="19"/>
                                        <columndefinition width="*"/>
                                    </grid.columndefinitions>
                                    <grid>
                                        <grid.layouttransform>
                                            <transformgroup>
                                                <transformgroup.children>
                                                    <transformcollection>
                                                        <rotatetransform angle="180"/>
                                                    </transformcollection>
                                                </transformgroup.children>
                                            </transformgroup>
                                        </grid.layouttransform>
                                        <ellipse x:name="circle" fill="{staticresource expander.static.circle.fill}" horizontalalignment="center" height="19" stroke="{staticresource expander.static.circle.stroke}" verticalalignment="center" width="19"/>
                                        <path x:name="arrow" data="m 1,1.5 l 4.5,5 l 8,1.5" horizontalalignment="center" snapstodevicepixels="false" stroke="{staticresource expander.static.arrow.stroke}" strokethickness="2" verticalalignment="center"/>
                                    </grid>
                                    <contentpresenter grid.column="1" horizontalalignment="left" margin="4,0,0,0" recognizesaccesskey="true" snapstodevicepixels="true" verticalalignment="center"/>
                                </grid>
                            </border>
                            <controltemplate.triggers>
                                <trigger property="ischecked" value="true">
                                    <setter property="data" targetname="arrow" value="m 1,4.5  l 4.5,1  l 8,4.5"/>
                                </trigger>
                                <trigger property="ismouseover" value="true">
                                    <setter property="stroke" targetname="circle" value="{staticresource expander.mouseover.circle.stroke}"/>
                                    <setter property="fill" targetname="circle" value="{staticresource expander.mouseover.circle.fill}"/>
                                    <setter property="stroke" targetname="arrow" value="{staticresource expander.mouseover.arrow.stroke}"/>
                                </trigger>
                                <trigger property="ispressed" value="true">
                                    <setter property="stroke" targetname="circle" value="{staticresource expander.pressed.circle.stroke}"/>
                                    <setter property="strokethickness" targetname="circle" value="1.5"/>
                                    <setter property="fill" targetname="circle" value="{staticresource expander.pressed.circle.fill}"/>
                                    <setter property="stroke" targetname="arrow" value="{staticresource expander.pressed.arrow.stroke}"/>
                                </trigger>
                                <trigger property="isenabled" value="false">
                                    <setter property="stroke" targetname="circle" value="{staticresource expander.disabled.circle.stroke}"/>
                                    <setter property="fill" targetname="circle" value="{staticresource expander.disabled.circle.fill}"/>
                                    <setter property="stroke" targetname="arrow" value="{staticresource expander.disabled.arrow.stroke}"/>
                                </trigger>
                            </controltemplate.triggers>
                        </controltemplate>
                    </setter.value>
                </setter>
            </style>
            <!--expanddirection=left,即向左展开时的头部样式-->
            <style x:key="expanderleftheaderstyle" targettype="{x:type togglebutton}">
                <setter property="template">
                    <setter.value>
                        <controltemplate targettype="{x:type togglebutton}">
                            <border padding="{templatebinding padding}">
                                <grid background="transparent" snapstodevicepixels="false">
                                    <grid.rowdefinitions>
                                        <rowdefinition height="19"/>
                                        <rowdefinition height="*"/>
                                    </grid.rowdefinitions>
                                    <grid>
                                        <grid.layouttransform>
                                            <transformgroup>
                                                <transformgroup.children>
                                                    <transformcollection>
                                                        <rotatetransform angle="90"/>
                                                    </transformcollection>
                                                </transformgroup.children>
                                            </transformgroup>
                                        </grid.layouttransform>
                                        <ellipse x:name="circle" fill="{staticresource expander.static.circle.fill}" horizontalalignment="center" height="19" stroke="{staticresource expander.static.circle.stroke}" verticalalignment="center" width="19"/>
                                        <path x:name="arrow" data="m 1,1.5 l 4.5,5 l 8,1.5" horizontalalignment="center" snapstodevicepixels="false" stroke="{staticresource expander.static.arrow.stroke}" strokethickness="2" verticalalignment="center"/>
                                    </grid>
                                    <contentpresenter horizontalalignment="center" margin="0,4,0,0" grid.row="1" recognizesaccesskey="true" snapstodevicepixels="true" verticalalignment="top"/>
                                </grid>
                            </border>
                            <controltemplate.triggers>
                                <trigger property="ischecked" value="true">
                                    <setter property="data" targetname="arrow" value="m 1,4.5  l 4.5,1  l 8,4.5"/>
                                </trigger>
                                <trigger property="ismouseover" value="true">
                                    <setter property="stroke" targetname="circle" value="{staticresource expander.mouseover.circle.stroke}"/>
                                    <setter property="fill" targetname="circle" value="{staticresource expander.mouseover.circle.fill}"/>
                                    <setter property="stroke" targetname="arrow" value="{staticresource expander.mouseover.arrow.stroke}"/>
                                </trigger>
                                <trigger property="ispressed" value="true">
                                    <setter property="stroke" targetname="circle" value="{staticresource expander.pressed.circle.stroke}"/>
                                    <setter property="strokethickness" targetname="circle" value="1.5"/>
                                    <setter property="fill" targetname="circle" value="{staticresource expander.pressed.circle.fill}"/>
                                    <setter property="stroke" targetname="arrow" value="{staticresource expander.pressed.arrow.stroke}"/>
                                </trigger>
                                <trigger property="isenabled" value="false">
                                    <setter property="stroke" targetname="circle" value="{staticresource expander.disabled.circle.stroke}"/>
                                    <setter property="fill" targetname="circle" value="{staticresource expander.disabled.circle.fill}"/>
                                    <setter property="stroke" targetname="arrow" value="{staticresource expander.disabled.arrow.stroke}"/>
                                </trigger>
                            </controltemplate.triggers>
                        </controltemplate>
                    </setter.value>
                </setter>
            </style>
            <!--expanddirection=down,即向下展开时的头部样式(一般默认向下展开)-->
            <style x:key="expanderdownheaderstyle" targettype="{x:type togglebutton}">
                <setter property="template">
                    <setter.value>
                        <controltemplate targettype="{x:type togglebutton}">
                            <border padding="{templatebinding padding}">
                                <grid background="transparent" snapstodevicepixels="false">
                                    <grid.columndefinitions>
                                        <columndefinition width="19"/>
                                        <columndefinition width="*"/>   <!--可变部分自适应存放文字-->
                                    </grid.columndefinitions>
                                    <!--下拉头部(ellipse和path组成圆圈箭头图标 contentpresenter为文字)-->
                                    <ellipse x:name="circle" fill="{staticresource expander.static.circle.fill}" horizontalalignment="center" height="19" stroke="{staticresource expander.static.circle.stroke}" verticalalignment="center" width="19"/>
                                    <path x:name="arrow" data="m 1,1.5 l 4.5,5 l 8,1.5" horizontalalignment="center" snapstodevicepixels="false" stroke="{staticresource expander.static.arrow.stroke}" strokethickness="2" verticalalignment="center"/>
                                    <contentpresenter grid.column="1" horizontalalignment="left" margin="4,0,0,0" recognizesaccesskey="true" snapstodevicepixels="true" verticalalignment="center"/>
                                </grid>
                            </border>
                            <controltemplate.triggers>
                                <!--选中时向下箭头变为为向上箭头-->
                                <trigger property="ischecked" value="true">
                                    <setter property="data" targetname="arrow" value="m 1,4.5  l 4.5,1  l 8,4.5"/>
                                </trigger>
                                <trigger property="ismouseover" value="true">
                                    <setter property="stroke" targetname="circle" value="{staticresource expander.mouseover.circle.stroke}"/>
                                    <setter property="fill" targetname="circle" value="{staticresource expander.mouseover.circle.fill}"/>
                                    <setter property="stroke" targetname="arrow" value="{staticresource expander.mouseover.arrow.stroke}"/>
                                </trigger>
                                <trigger property="ispressed" value="true">
                                    <setter property="stroke" targetname="circle" value="{staticresource expander.pressed.circle.stroke}"/>
                                    <setter property="strokethickness" targetname="circle" value="1.5"/>
                                    <setter property="fill" targetname="circle" value="{staticresource expander.pressed.circle.fill}"/>
                                    <setter property="stroke" targetname="arrow" value="{staticresource expander.pressed.arrow.stroke}"/>
                                </trigger>
                                <trigger property="isenabled" value="false">
                                    <setter property="stroke" targetname="circle" value="{staticresource expander.disabled.circle.stroke}"/>
                                    <setter property="fill" targetname="circle" value="{staticresource expander.disabled.circle.fill}"/>
                                    <setter property="stroke" targetname="arrow" value="{staticresource expander.disabled.arrow.stroke}"/>
                                </trigger>
                            </controltemplate.triggers>
                        </controltemplate>
                    </setter.value>
                </setter>
            </style>
            <!--键盘焦点样式-->
            <style x:key="expanderheaderfocusvisual">
                <setter property="control.template">
                    <setter.value>
                        <controltemplate>
                            <border>
                                <rectangle margin="0" snapstodevicepixels="true" stroke="black" strokethickness="1" strokedasharray="1 2"/>
                            </border>
                        </controltemplate>
                    </setter.value>
                </setter>
            </style>
            <!--expander样式-->
            <style x:key="expanderstyle" targettype="{x:type expander}">
                <setter property="foreground" value="{dynamicresource {x:static systemcolors.controltextbrushkey}}"/>
                <setter property="background" value="transparent"/>
                <setter property="horizontalcontentalignment" value="stretch"/>
                <setter property="verticalcontentalignment" value="stretch"/>
                <setter property="borderbrush" value="transparent"/>
                <setter property="borderthickness" value="1"/>
                <setter property="template">
                    <setter.value>
                        <!--控件模板-->
                        <controltemplate targettype="{x:type expander}">
                            <border borderbrush="{templatebinding borderbrush}" borderthickness="{templatebinding borderthickness}" background="{templatebinding background}" cornerradius="3" snapstodevicepixels="true">
                                <dockpanel>
                                    <!--头部标题-->
                                    <togglebutton x:name="headersite" contenttemplate="{templatebinding headertemplate}" contenttemplateselector="{templatebinding headertemplateselector}" content="{templatebinding header}" dockpanel.dock="top" foreground="{templatebinding foreground}" fontweight="{templatebinding fontweight}" focusvisualstyle="{staticresource expanderheaderfocusvisual}" fontstyle="{templatebinding fontstyle}" fontstretch="{templatebinding fontstretch}" fontsize="{templatebinding fontsize}" fontfamily="{templatebinding fontfamily}" horizontalcontentalignment="{templatebinding horizontalcontentalignment}" ischecked="{binding isexpanded, mode=twoway, relativesource={relativesource templatedparent}}" margin="1" minwidth="0" minheight="0" padding="{templatebinding padding}" style="{staticresource expanderdownheaderstyle}" verticalcontentalignment="{templatebinding verticalcontentalignment}"/>
                                    <!--下拉内容-->
                                    <contentpresenter x:name="expandsite" dockpanel.dock="bottom" focusable="false" horizontalalignment="{templatebinding horizontalcontentalignment}" margin="{templatebinding padding}" visibility="collapsed" verticalalignment="{templatebinding verticalcontentalignment}"/>
                                </dockpanel>
                            </border>
                            <!--触发器-->
                            <controltemplate.triggers>
                                <!--内容展开则显示隐藏内容-->
                                <trigger property="isexpanded" value="true">
                                    <setter property="visibility" targetname="expandsite" value="visible"/>
                                </trigger>
                                <!--向左、右、上展开修改dockpanel中标题和内容布局方式实现-->
                                <trigger property="expanddirection" value="right">
                                    <setter property="dockpanel.dock" targetname="expandsite" value="right"/>
                                    <setter property="dockpanel.dock" targetname="headersite" value="left"/>
                                    <setter property="style" targetname="headersite" value="{staticresource expanderrightheaderstyle}"/>
                                </trigger>
                                <trigger property="expanddirection" value="up">
                                    <setter property="dockpanel.dock" targetname="expandsite" value="top"/>
                                    <setter property="dockpanel.dock" targetname="headersite" value="bottom"/>
                                    <setter property="style" targetname="headersite" value="{staticresource expanderupheaderstyle}"/>
                                </trigger>
                                <trigger property="expanddirection" value="left">
                                    <setter property="dockpanel.dock" targetname="expandsite" value="left"/>
                                    <setter property="dockpanel.dock" targetname="headersite" value="right"/>
                                    <setter property="style" targetname="headersite" value="{staticresource expanderleftheaderstyle}"/>
                                </trigger>
                                <trigger property="isenabled" value="false">
                                    <setter property="foreground" value="{dynamicresource {x:static systemcolors.graytextbrushkey}}"/>
                                </trigger>
                            </controltemplate.triggers>
                        </controltemplate>
                    </setter.value>
                </setter>
            </style>

 

  知道了expander默认控件模板的构成就可以根据需要修改,轻松构建自定义的导航栏等功能(建议只修改需要修改的部分)。