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

c# WPF中自定义加载时实现带动画效果的Form和FormItem

程序员文章站 2022-03-23 18:43:25
背景  今天我们来谈一下我们自定义的一组wpf控件form和formitem,然后看一下如何自定义一组完整地组合wpf控件,在我们很多界面显示的时候我们需要同时显示文本、图片并且我们需要将这些按照特定...

背景

  今天我们来谈一下我们自定义的一组wpf控件form和formitem,然后看一下如何自定义一组完整地组合wpf控件,在我们很多界面显示的时候我们需要同时显示文本、图片并且我们需要将这些按照特定的顺序整齐的排列在一起,这样的操作当然通过定义grid和stackpanel然后组合在一起当然也是可以的,我们的这一组控件就是将这个过程组合到一个form和formitem中间去,从而达到这样的效果,我们首先来看看这组控件实现的效果。

一 动画效果

c# WPF中自定义加载时实现带动画效果的Form和FormItem

  看了这个效果之后我们来看看怎么来使用form和formitem控件,后面再进一步分析这两个控件的一些细节信息。

<xui:tabcontrol canvas.left="356" canvas.top="220">
                   <xui:tabitem header="overview">
                       <xui:form margin="2" >
                           <xui:formitem content="test1" height="30"></xui:formitem>
                           <xui:formitem content="test2" height="30"></xui:formitem>
                           <xui:formitem content="test3" height="30"></xui:formitem>
                       </xui:form>
                   </xui:tabitem>
                   <xui:tabitem header="plumbing">
                       <xui:form margin="2" columns="2" rows="2">
                           <xui:formitem content="demo1" height="30" margin="5 2"></xui:formitem>
                           <xui:formitem content="demo2" height="30" margin="5 2"></xui:formitem>
                           <xui:formitem content="demo3" height="30" margin="5 2"></xui:formitem>
                           <xui:formitem content="demo4" height="30" margin="5 2"></xui:formitem>
                       </xui:form>
                   </xui:tabitem>
                   <xui:tabitem header="clean">
                       <xui:textbox text="test2" width="220" height=" 30" margin="5"></xui:textbox>
                   </xui:tabitem>
               </xui:tabcontrol>

  这个里面xui命名控件是我们的自定义控件库的命名空间,这个里面的tablecontrol也是一种特殊的自定义的tablecontrol,关于这个tablecontrol我们后面也会进一步分析。

二 自定义控件实现

  按照上面的顺序我们先来分析form控件,然后再分析formitem控件的实现细节

  2.1 form

  通过上面的代码我们发现form是可以承载formitem的,所以它是一个可以承载子控件的容器控件,这里form是集成itemscontrol的,我们来看看具体的代码

public class form : itemscontrol
    {
        static form()
        {
            defaultstylekeyproperty.overridemetadata(typeof(form), new frameworkpropertymetadata(typeof(form)));
        }
 
        public double headerwidth
        {
            get { return (double)getvalue(headerwidthproperty); }
            set { setvalue(headerwidthproperty, value); }
        }
 
        // using a dependencyproperty as the backing store for headerwidth.  this enables animation, styling, binding, etc...
        public static readonly dependencyproperty headerwidthproperty =
            dependencyproperty.register("headerwidth", typeof(double), typeof(form), new propertymetadata(70d));
 
        public int rows
        {
            get { return (int)getvalue(rowsproperty); }
            set { setvalue(rowsproperty, value); }
        }
 
        // using a dependencyproperty as the backing store for rows.  this enables animation, styling, binding, etc...
        public static readonly dependencyproperty rowsproperty =
            dependencyproperty.register("rows", typeof(int), typeof(form), new propertymetadata(0));
 
        public int columns
        {
            get { return (int)getvalue(columnsproperty); }
            set { setvalue(columnsproperty, value); }
        }
 
        // using a dependencyproperty as the backing store for columns.  this enables animation, styling, binding, etc...
        public static readonly dependencyproperty columnsproperty =
            dependencyproperty.register("columns", typeof(int), typeof(form), new propertymetadata(1));
 
    }  

  然后我们再来看看form的样式文件

<style targettype="local:form">
        <setter property="padding" value="10"></setter>
        <setter property="template">
            <setter.value>
                <controltemplate targettype="local:form">
                    <scrollviewer background="#eee" horizontalscrollbarvisibility="disabled" verticalscrollbarvisibility="auto">
                        <uniformgrid columns="{templatebinding columns}" rows="{templatebinding rows}" isitemshost="true" background="transparent" verticalalignment="top" margin="{templatebinding padding}"></uniformgrid>
                    </scrollviewer>
                </controltemplate>
            </setter.value>
        </setter>
        <setter property="itemtemplate">
            <setter.value>
                <datatemplate>
                    <contentpresenter content="{binding}" focusable="false"></contentpresenter>
                </datatemplate>
            </setter.value>
        </setter>
    </style>  

  这里我们使用uniformgrid作为内容承载容器,所以我们现在清楚了为什么需要定义columns和rows这两个依赖项属性了,这个uniformgrid是嵌套在scrollerviewer中的,所以如果其子控件超出了一定范围,其子控件外面是会显示滚动条的。

  2.2 formitem

  formitem是从listboxitem继承而来,而listboxitem又是从contentcontrol继承而来的,所以可以添加到任何具有content属性的控件中去,常见的listboxitem可以放到listbox中,也可以放到itemscontrol中去,listboxitem可以横向和treeviewitem进行比较,只不过treeviewitem是直接从headereditemscontrol继承过来的,然后再继承自itemscontrol。两者有很多的共同之处,可以做更多的横向比较,我们今天只是来讲listboxitem,首先看看我们使用的样式,这里贴出前端代码:

<style targettype="local:formitem">
        <setter property="margin" value="0 0 0 15"></setter>
        <setter property="background" value="#fff"></setter>
        <setter property="height" value="50"></setter>
        <setter property="horizontalalignment" value="stretch"></setter>
        <setter property="verticalalignment" value="stretch"></setter>
        <setter property="padding" value="6"></setter>
        <setter property="foreground" value="{staticresource darkcolor}"></setter>
        <setter property="template">
            <setter.value>
                <controltemplate targettype="local:formitem">
                    <grid background="{templatebinding background}" height="{templatebinding height}">
                        <grid.columndefinitions>
                            <columndefinition width="{binding headerwidth,relativesource={relativesource mode=findancestor,ancestortype=local:form}}"></columndefinition>
                            <columndefinition width="*"></columndefinition>
                        </grid.columndefinitions>
                        <rectangle width="3" horizontalalignment="left" fill="{staticresource highlight}"></rectangle>
                        <stackpanel verticalalignment="center" horizontalalignment="left" margin="13 0 0 0" orientation="horizontal">
                            <image x:name="icon" source="{templatebinding icon}" width="24" height="24" margin="0 0 10 0" verticalalignment="center" horizontalalignment="left"></image>
                            <textblock text="{templatebinding title}" foreground="{templatebinding foreground}" verticalalignment="center" horizontalalignment="left"></textblock>
                        </stackpanel>
                        <contentpresenter margin="{templatebinding padding}" grid.column="1" horizontalalignment="{templatebinding horizontalalignment}" verticalalignment="{templatebinding verticalalignment}"></contentpresenter>
                    </grid>
                    <controltemplate.triggers>
                        <trigger property="icon" value="{x:null}">
                            <setter property="visibility" value="collapsed" targetname="icon"></setter>
                        </trigger>
                    </controltemplate.triggers>
                </controltemplate>
            </setter.value>
        </setter>
    </style>  

  这里我们重写了listboxitem 的controltemplate,我们需要注意的一个地方就是我们使用了

<contentpresenter margin="{templatebinding padding}" grid.column="1" horizontalalignment="{templatebinding horizontalalignment}" verticalalignment="{templatebinding verticalalignment}"></contentpresenter> 

来替代listboxitem的content,我们需要始终记住,只有控件拥有content属性才能使用contentpresenter ,这个属性是用来呈现控件的content。

     另外一个需要重点介绍的就是formitem这个类中的代码,这个控件在加载的时候所有的效果都是在后台中进行加载的,首先贴出相关的类的实现,然后再做进一步的分析。

using system;
using system.collections.generic;
using system.linq;
using system.text;
using system.threading.tasks;
using system.windows;
using system.windows.controls;
using system.windows.media;
using system.windows.media.animation;
 
namespace x.ui
{
    public class formitem : listboxitem
    {
        static formitem()
        {
            defaultstylekeyproperty.overridemetadata(typeof(formitem), new frameworkpropertymetadata(typeof(formitem)));          
        }
 
        public formitem()
        {
            system.windows.media.translatetransform transform = ensurerendertransform<system.windows.media.translatetransform>(this);
            transform.x = transform.y = 100;
            opacity = 0;
 
            isvisiblechanged += formitem_isvisiblechanged;
        }
 
        void formitem_isvisiblechanged(object sender, dependencypropertychangedeventargs e)
        {
            if (this.parent is form)
            {
                if (!isvisible)
                {
                    int index = (this.parent as form).items.indexof(this);
                    system.windows.media.translatetransform transform = ensurerendertransform<system.windows.media.translatetransform>(this);
                    doubleanimation da = new doubleanimation()
                    {
                        from = 0,
                        to = 100,
                        easingfunction = new circleease { easingmode = easingmode.easeout }
                    };
                    transform.beginanimation(system.windows.media.translatetransform.xproperty, da);
                    transform.beginanimation(system.windows.media.translatetransform.yproperty, da);
                    doubleanimation daopacity = new doubleanimation
                    {
                        from = 1,
                        to = 0,
                    };
                    this.beginanimation(uielement.opacityproperty, daopacity);
                }
                else
                {
                    int index = (this.parent as form).items.indexof(this);
                    system.windows.media.translatetransform transform = ensurerendertransform<system.windows.media.translatetransform>(this);
                    doubleanimation da = new doubleanimation()
                    {
                        from = 100,
                        to = 0,
                        begintime = timespan.frommilliseconds(100 * (index + 1)),
                        duration = timespan.frommilliseconds(666),
                        easingfunction = new circleease { easingmode = easingmode.easeout }
                    };
                    transform.beginanimation(system.windows.media.translatetransform.xproperty, da);
                    transform.beginanimation(system.windows.media.translatetransform.yproperty, da);
                    doubleanimation daopacity = new doubleanimation
                    {
                        from = 0,
                        to = 1,
                        begintime = timespan.frommilliseconds(100 * (index + 1)),
                        duration = timespan.frommilliseconds(666),
                        easingfunction = new circleease { easingmode = easingmode.easeout }
                    };
                    this.beginanimation(uielement.opacityproperty, daopacity);
                }
            }
        }
 
        private t ensurerendertransform<t>(uielement uitarget)
            where t : transform
        {
            if (uitarget.rendertransform is t)
                return uitarget.rendertransform as t;
            else
            {
                t instance = typeof(t).assembly.createinstance(typeof(t).fullname) as t;
                uitarget.rendertransform = instance;
                return instance;
            }
        }
 
        public string title
        {
            get { return (string)getvalue(titleproperty); }
            set { setvalue(titleproperty, value); }
        }
 
        // using a dependencyproperty as the backing store for title.  this enables animation, styling, binding, etc...
        public static readonly dependencyproperty titleproperty =
            dependencyproperty.register("title", typeof(string), typeof(formitem), new propertymetadata(""));
 
 
        public imagesource icon
        {
            get { return (imagesource)getvalue(iconproperty); }
            set { setvalue(iconproperty, value); }
        }
 
        // using a dependencyproperty as the backing store for icon.  this enables animation, styling, binding, etc...
        public static readonly dependencyproperty iconproperty =
            dependencyproperty.register("icon", typeof(imagesource), typeof(formitem), new propertymetadata(null));
 
    }
}  

     这里在formitem的构造函数中,添加了一个isvisiblechanged事件,这个事件会在加载当前控件的时候发生,另外当当前控件的属性值发生变化的时候会触发该效果。其实效果就是同时在x和y方向做一个平移的效果,这个也是一个常用的效果。

     我们重点讨论的是下面的这段代码:     

private t ensurerendertransform<t>(uielement uitarget)
           where t : transform
       {
           if (uitarget.rendertransform is t)
               return uitarget.rendertransform as t;
           else
           {
               t instance = typeof(t).assembly.createinstance(typeof(t).fullname) as t;
               uitarget.rendertransform = instance;
               return instance;
           }
       }

  这里我们创建translatetransform的时候是使用的system.windows.media.translatetransform transform = ensurerendertransform<system.windows.media.translatetransform>(this);这个方法,而不是每次都new一个对象,每次new一个对象的效率是很低的,而且会占据内存,我们如果已经创建过当前对象完全可以重复利用,这里我们使用了带泛型参数的函数来实现当前效果,typeof(t).assembly.createinstance(typeof(t).fullname) as t,核心是通过程序集来创建对象,这种方式我们也是经常会使用的,比如我们可以通过获取应用程序级别的程序集来通过activator.createinstance来创建窗体等一系列的对象,这种通过反射的机制来扩展的方法是我们需要特别留意的,另外写代码的时候必须注重代码的质量和效率,而不仅仅是实现了某一个功能,这个在以后的开发过程中再一点点去积累去吸收。

以上就是c# wpf中自定义加载时实现带动画效果的form和formitem的详细内容,更多关于c# wpf实现带动画效果的form和formitem的资料请关注其它相关文章!