背景

  今天我们来谈一下我们自定义的一组wpf控件form和formitem,然后看一下如何自定义一组完整地组合wpf控件,在我们很多界面显示的时候我们需要同时显示文本、图片并且我们需要将这些按照特定的顺序整齐的排列在一起,这样的操作当然通过定义grid和stackpanel然后组合在一起当然也是可以的,我们的这一组控件就是将这个过程组合到一个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的资料请关注www.887551.com其它相关文章!