背景

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

一 动画效果

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


                   
                       
                           
                           
                           
                       
                   
                   
                       
                           
                           
                           
                           
                       
                   
                   
                       
                   
               

这个里面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的样式文件




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

2.2 FormItem

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




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

 

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

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

c# WPF中自定义加载时实现带动画效果的Form和FormItem的更多相关文章

  1. 使用 C# 9 的records作为强类型ID - 路由和查询参数

    上一篇文章,我介绍了使用 C# 9 的record类型作为强类型id,非常简洁public record Produ......

  2. c#在sqlserver中使用EF框架

    vs2017,sqlserver2017(localdb)调试通过。在sqlserver中创建数据库d1,表t1如下......

  3. c#里面的AES加密解密

    C#, Java, PHP, Python和Javascript几种语言的AES加密解密实现更多1AESJavasc......

  4. C# AE之返回上一级和下一级的实战操作

    我就废话不多说了,大家还是直接看代码吧~try{//判断是否可以返回上一视图if (mapControl.Activ......

  5. C#基于jwt实现分布式登录

    一、传统的session登录在服务器存储一份用户登录的信息,这份登录信息会在响应时传递给浏览器,告诉其保存为cook......

  6. 计算机网络安全 —— C# 使用谷歌身份验证器(Google Authenticator)

    一、Google Authenticator 基本概念Google Authenticator是谷歌推出的一款动态口......

  7. C#中DataGridView导出Excel的两种方法

    第一种是用数据流导出:#regionSaveFileDialog saveFileDialog = new Save......

  8. c#定时执行程序代码

    在一般的项目中我们很少用到c#实现每隔规定时间自动执行程序代码,但是如果你经历的项目多,或者应用程序做的比较多的话,......

  9. c#生成缩略图代码

    public void SaveThumbnail(string path, string thumbnailPat......

  10. C# StreamReader类实现读取文件的方法

    在 C# 语言中 StreamReader 类用于从流中读取字符串。它继承自 TextReader 类。Stream......

随机推荐

  1. Android 基于agora 开发视频会议的代码

    一、概述参照官方demo,基于agora开发,输入会议号(频道)和显示名称 参会,可设置参会选项。支持用户注册和登录......

  2. 在.NET Core 中使用Quartz.NET

    Quartz.NET是功能齐全的开源作业调度系统,可用于最小的应用程序到大型企业系统。Quartz.NET具有三个主......

  3. Python实现微信表情包炸群

    Python实现微信表情包炸群# -*- coding = utf-8 -*- # @Time : 2021/1/2......

  4. Java Object类 和 String类 常见问答

    Java常见对象 Object类 和 String类 常见问答 6k字+总结写在最前面这个项目是从20年末就立好的 ......

  5. Linux core dump使用

    什么是 core dump?core dump是一个当进程意外终止时包含进程内存内容的文件。当程序崩溃的时候,cor......

  6. C#中DataGridView导出Excel的两种方法

    第一种是用数据流导出:#regionSaveFileDialog saveFileDialog = new Save......

  7. 利用python+request通过接口实现人员通行记录上传功能

    前言:脚本中包含以下几点常用功能:(1)实时获取当前时间(2)while循环提交(3)上传图片文件一、上述功能解释:......

  8. 浅析python字符串前加r、f、u、l 的区别

    先给大家介绍下Python 字符串前面加u,r,b,f的含义(字符串前缀)1、字符串前加 u例:u"我是含......

  9. 自己动手实现java断点/单步调试(二)

    自从上一篇《自己动手实现java断点/单步调试(一)》是时候应该总结一下JDI的事件了事件类型描述ClassPrep......

  10. synchronized详解

    synchronized是Java多线程中元老级的锁,也是面试的高频考点,让我们来详细了解synchronized吧......