it编程 > App开发 > Windows Phone

WPF实现Drawer抽屉控件

223人参与 2024-11-09 Windows Phone

drawer 抽屉控件的实现

框架支持.net4 至 .net8

visual studio 2022;

抽屉控件的逻辑实现

定义了一个名为 drawer 的自定义控件,继承自 headeredcontentcontrol,允许用户在应用程序中创建可展开和收起的抽屉。抽屉的显示和隐藏动画通过 storyboard 实现,支持从不同方向()展开和收起。

1.定义模板

使用 templatepart 特性定义了两个模板:borderheadertemplatename 和 bordermarktemplatename,分别代表抽屉的蒙板部分。

[templatepart(name = borderheadertemplatename, type = typeof(border))]
[templatepart(name = bordermarktemplatename, type = typeof(border))]
public class drawer : headeredcontentcontrol

2.定义依赖属性

public static readonly dependencyproperty edgepositionproperty =
    dependencyproperty.register("position", typeof(position), typeof(drawer),
        new propertymetadata(position.left));

public static readonly dependencyproperty isopenproperty =
    dependencyproperty.register("isopen", typeof(bool), typeof(drawer),
        new propertymetadata(false, onisopenchanged));

3.属性实现

public position position
{
    get => (position) getvalue(edgepositionproperty);
    set => setvalue(edgepositionproperty, value);
}

public bool isopen
{
    get => (bool) getvalue(isopenproperty);
    set => setvalue(isopenproperty, value);
}

4.状态变化处理

onisopenchanged 方法根据 isopen 属性的值,控制抽屉的显示或隐藏和动画效果。

private static void onisopenchanged(dependencyobject d, dependencypropertychangedeventargs e)
{
    var ctrl = d as drawer;
    if (ctrl != null)
    {
        if (ctrl.isopen)
        {
            ctrl._headerborder.visibility = visibility.visible;
            ctrl._enterstoryboard.begin();
        }
        else
        {
            ctrl._exitstoryboard.begin();
        }
    }
}

5.模板应用

onapplytemplate 方法在控件模板应用时调用,获取模板中的 border 部件并注册 loaded 和 mousedown 事件。

public override void onapplytemplate()
{
    base.onapplytemplate();
    _headerborder = gettemplatechild(borderheadertemplatename) as border;
    if (_headerborder != null)
        _headerborder.loaded += headerborder_loaded;
    _markborder = gettemplatechild(bordermarktemplatename) as border;
    if (_markborder != null)
        _markborder.mousedown += markborder_mousedown;
}

6.动画实现

headerborder_loaded 方法根据 position 的不同,设置不同的动画,并根据属性值控制动画的执行。

        private void headerborder_loaded(object sender, routedeventargs e)
        {
            translatetransform translatetransform;
            doubleanimation animation, exitanimation;
            switch (position)
            {
                case position.left:
                case position.right:
                    _headerwidth = _headerborder.actualwidth;
                    if (position == position.left)
                        translatetransform = new translatetransform(-_headerwidth, 0);
                    else
                        translatetransform = new translatetransform(_headerwidth, 0);
                    _headerborder.rendertransform = new transformgroup
                    {
                        children = new transformcollection {translatetransform}
                    };
                    animation = new doubleanimation
                    {
                        from = position == position.left ? -_headerwidth : _headerwidth,
                        to = 0,
                        duration = timespan.frommilliseconds(300)
                    };

                    storyboard.settarget(animation, _headerborder);
                    storyboard.settargetproperty(animation, new propertypath("rendertransform.children[0].x"));
                    _enterstoryboard = new storyboard();
                    _enterstoryboard.children.add(animation);

                    exitanimation = new doubleanimation
                    {
                        from = 0,
                        to = position == position.left ? -_headerwidth : _headerwidth,
                        duration = timespan.frommilliseconds(300)
                    };

                    storyboard.settarget(exitanimation, _headerborder);
                    storyboard.settargetproperty(exitanimation, new propertypath("rendertransform.children[0].x"));
                    _exitstoryboard = new storyboard();
                    _exitstoryboard.completed += delegate
                    {
                        if (!isopen)
                            _headerborder.visibility = visibility.collapsed;
                    };
                    _exitstoryboard.children.add(exitanimation);
                    break;
                case position.top:
                case position.bottom:
                    _headerheight = _headerborder.actualheight;
                    if (position == position.top)
                        translatetransform = new translatetransform(0, -_headerheight);
                    else
                        translatetransform = new translatetransform(0, _headerheight);
                    _headerborder.rendertransform = new transformgroup
                    {
                        children = new transformcollection {translatetransform}
                    };
                    animation = new doubleanimation
                    {
                        from = position == position.top ? -_headerheight : _headerheight,
                        to = 0,
                        duration = timespan.frommilliseconds(300)
                    };

                    storyboard.settarget(animation, _headerborder);
                    storyboard.settargetproperty(animation, new propertypath("rendertransform.children[0].y"));
                    _enterstoryboard = new storyboard();
                    _enterstoryboard.children.add(animation);

                    exitanimation = new doubleanimation
                    {
                        from = 0,
                        to = position == position.top ? -_headerheight : _headerheight,
                        duration = timespan.frommilliseconds(300)
                    };

                    storyboard.settarget(exitanimation, _headerborder);
                    storyboard.settargetproperty(exitanimation, new propertypath("rendertransform.children[0].y"));
                    _exitstoryboard = new storyboard();
                    _exitstoryboard.completed += delegate
                    {
                        if (!isopen)
                            _headerborder.visibility = visibility.collapsed;
                    };
                    _exitstoryboard.children.add(exitanimation);
                    break;
            }

            _headerborder.visibility = visibility.collapsed;
            _headerborder.loaded -= headerborder_loaded;
        }

github 源码地址

gitee 源码地址

抽屉控件的样式实现

position 属性的触发器:根据 position 属性的值(toprightbottom),调整 part_header 的对齐方式。

isopen 属性的触发器:如果 isopen 为 false,将 part_mark 的可见性设置为 collapsed,在抽屉关闭时隐藏。

<style
    x:key="wd.drawer"
    basedon="{staticresource wd.controlbasicstyle}"
    targettype="{x:type controls:drawer}">
    <setter property="background" value="{dynamicresource wd.backgroundsolidcolorbrush}" />
    <setter property="borderbrush" value="{dynamicresource wd.basesolidcolorbrush}" />
    <setter property="borderthickness" value="1" />
    <setter property="template">
        <setter.value>
            <controltemplate targettype="{x:type controls:drawer}">
                <controls:wdborder
                    x:name="border"
                    margin="{templatebinding margin}"
                    background="{templatebinding background}"
                    borderbrush="{templatebinding borderbrush}"
                    borderthickness="{templatebinding borderthickness}"
                    cornerradius="{binding path=(helpers:elementhelper.cornerradius), relativesource={relativesource templatedparent}}"
                    snapstodevicepixels="true">
                    <controls:smallpanel clip="{binding relativesource={relativesource ancestortype=controls:wdborder}, path=contentclip}">
                        <border x:name="part_content">
                            <contentpresenter content="{templatebinding content}" />
                        </border>
                        <border
                            x:name="part_mark"
                            background="{dynamicresource wd.primarytextsolidcolorbrush}"
                            opacity=".5" />
                        <border
                            x:name="part_header"
                            horizontalalignment="left"
                            background="{templatebinding background}"
                            effect="{staticresource wd.normalshadowdepth}">
                            <contentpresenter content="{templatebinding header}" />
                        </border>
                    </controls:smallpanel>
                </controls:wdborder>
                <controltemplate.triggers>
                    <trigger property="position" value="top">
                        <setter targetname="part_header" property="horizontalalignment" value="stretch" />
                        <setter targetname="part_header" property="verticalalignment" value="top" />
                    </trigger>
                    <trigger property="position" value="right">
                        <setter targetname="part_header" property="horizontalalignment" value="right" />
                        <setter targetname="part_header" property="verticalalignment" value="stretch" />
                    </trigger>
                    <trigger property="position" value="bottom">
                        <setter targetname="part_header" property="horizontalalignment" value="stretch" />
                        <setter targetname="part_header" property="verticalalignment" value="bottom" />
                    </trigger>
                    <trigger property="isopen" value="false">
                        <setter targetname="part_mark" property="visibility" value="collapsed" />
                    </trigger>
                </controltemplate.triggers>
            </controltemplate>
        </setter.value>
    </setter>
</style>

示例

示例引入 wpfdevelopers 的 nuget

<usercontrol
    x:class="wpfdevelopers.samples.exampleviews.drawerexample"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:controls="clr-namespace:wpfdevelopers.samples.controls"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:local="clr-namespace:wpfdevelopers.samples.exampleviews"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:wd="https://github.com/wpfdevelopersorg/wpfdevelopers"
    d:designheight="450"
    d:designwidth="800"
    mc:ignorable="d">
    <controls:codeviewer>
        <grid>
            <grid.columndefinitions>
                <columndefinition />
                <columndefinition />
            </grid.columndefinitions>
            <grid.rowdefinitions>
                <rowdefinition />
                <rowdefinition />
            </grid.rowdefinitions>
            <wd:drawer
                x:name="mydrawertop"
                margin="2"
                position="top">
                <wd:drawer.header>
                    <grid height="120">
                        <stackpanel horizontalalignment="center">
                            <textblock fontsize="18" text="drawer" />
                            <button horizontalalignment="center" content="contents..." />
                        </stackpanel>
                    </grid>
                </wd:drawer.header>
                <wd:drawer.content>
                    <stackpanel horizontalalignment="center" verticalalignment="bottom">
                        <textblock text="抽屉从顶部置滑出,点击遮罩区关闭。" />
                        <button
                            margin="0,10"
                            horizontalalignment="center"
                            click="buttontop_click"
                            content="open"
                            style="{staticresource wd.primarybutton}" />
                    </stackpanel>
                </wd:drawer.content>
            </wd:drawer>
            <wd:drawer
                x:name="mydrawerbottom"
                grid.column="1"
                margin="2"
                wd:elementhelper.cornerradius="3"
                position="bottom">
                <wd:drawer.header>
                    <grid height="130">
                        <stackpanel horizontalalignment="center">
                            <textblock fontsize="18" text="drawer" />
                            <button
                                horizontalalignment="center"
                                wd:elementhelper.cornerradius="3"
                                content="contents..."
                                style="{staticresource wd.dangerdefaultbutton}" />
                        </stackpanel>
                    </grid>
                </wd:drawer.header>
                <wd:drawer.content>
                    <stackpanel horizontalalignment="center">
                        <textblock text="抽屉从底部滑出,点击遮罩区关闭。" />
                        <button
                            margin="0,10"
                            horizontalalignment="center"
                            wd:elementhelper.cornerradius="3"
                            click="buttonbottom_click"
                            content="open"
                            style="{staticresource wd.dangerprimarybutton}" />
                    </stackpanel>
                </wd:drawer.content>
            </wd:drawer>

            <wd:drawer
                x:name="mydrawerleft"
                grid.row="1"
                margin="2"
                wd:elementhelper.cornerradius="3"
                position="left">
                <wd:drawer.header>
                    <grid width="140" background="hotpink">
                        <stackpanel>
                            <textblock
                                horizontalalignment="center"
                                fontsize="18"
                                text="drawer" />
                            <button
                                horizontalalignment="center"
                                wd:elementhelper.cornerradius="3"
                                content="contents..."
                                style="{staticresource wd.successdefaultbutton}" />
                        </stackpanel>
                    </grid>
                </wd:drawer.header>
                <wd:drawer.content>
                    <stackpanel horizontalalignment="center">
                        <textblock text="抽屉从左侧滑出,点击遮罩区关闭。" />
                        <button
                            margin="0,10"
                            horizontalalignment="center"
                            wd:elementhelper.cornerradius="3"
                            click="buttonleft_click"
                            content="open"
                            style="{staticresource wd.successprimarybutton}" />
                    </stackpanel>
                </wd:drawer.content>
            </wd:drawer>
            <wd:drawer
                x:name="mydrawerright"
                grid.row="1"
                grid.column="1"
                margin="2"
                position="right">
                <wd:drawer.header>
                    <grid width="100">
                        <stackpanel horizontalalignment="center">
                            <textblock
                                horizontalalignment="center"
                                fontsize="18"
                                text="drawer" />
                            <button
                                horizontalalignment="center"
                                content="contents..."
                                style="{staticresource wd.warningdefaultbutton}" />
                        </stackpanel>
                    </grid>
                </wd:drawer.header>
                <wd:drawer.content>
                    <stackpanel horizontalalignment="center">
                        <textblock text="抽屉从右侧滑出,点击遮罩区关闭。" />
                        <button
                            margin="0,10"
                            horizontalalignment="center"
                            click="buttonright_click"
                            content="open"
                            style="{staticresource wd.warningprimarybutton}" />
                    </stackpanel>
                </wd:drawer.content>
            </wd:drawer>
        </grid>
        <controls:codeviewer.sourcecodes>
            <controls:sourcecodemodel codesource="/wpfdevelopers.samplescode;component/exampleviews/drawerexample.xaml" codetype="xaml" />
            <controls:sourcecodemodel codesource="/wpfdevelopers.samplescode;component/exampleviews/drawerexample.xaml.cs" codetype="csharp" />
        </controls:codeviewer.sourcecodes>
    </controls:codeviewer>
</usercontrol>

效果图

到此这篇关于wpf实现drawer抽屉控件的文章就介绍到这了,更多相关wpf抽屉控件内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!

(0)
打赏 微信扫一扫 微信扫一扫

您想发表意见!!点此发布评论

推荐阅读

WPF实现图片按像素拉伸

11-04

WPF如何在圆形上优雅地添加刻度线

11-22

WPF仿Tabcontrol实现切换多个不同View

12-01

WPF实现绘制饼状统计图的示例代码

10-21

WPF实现数据绑定的几种方法

12-07

WPF实现自定义控件的几种方法

12-07

猜你喜欢

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

发表评论