WPF自定义控件-按钮-DoubleAnimation动画

效果展示

在这里插入图片描述
最近在看Wpf的书,初了解Wpf,也没做过C#和Wpf的项目。
如果下述有不妥的地方请多多包含,有错误,还请指教。

原理

自定义控件继承Button,模板为两个重叠的Border

下面为其中一个样式的代码

  1. 自定义控件文件(MyButton.cs),自定生成的代码先暂时什么都不用改
using System.Windows;
using System.Windows.Controls;

namespace CSDN01
{
    public class MyButton : Button
    {
        static MyButton()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(MyButton), new FrameworkPropertyMetadata(typeof(MyButton)));
        }
    }
}
  1. 项目2,资源字典文件(TemplateMyButton.xaml),我暂时先把属性设成固定值,后期再改成依赖属性
    属性UseLayoutRounding=“False”
    的设置是一个注意点(后续♦1处说明)
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:local="clr-namespace:CSDN01">
    <ControlTemplate x:Key="TemplateMyButton" TargetType="{x:Type local:MyButton}">
        <Grid UseLayoutRounding="False">
            <Border x:Name="bd1" Background="LightCyan">
                <TextBlock Text="按钮" FontSize="12" Foreground="DarkCyan"
                               HorizontalAlignment="Center" VerticalAlignment="Center"/>
            </Border>
            <Border x:Name="bd2" Background="DarkCyan" Height="0">
                <TextBlock Text="按钮" FontSize="12" Foreground="LightCyan"
                               HorizontalAlignment="Center" VerticalAlignment="Center"/>
            </Border>
        </Grid>
    </ControlTemplate>
</ResourceDictionary>
  1. App.xaml里添加资源字典文件
<Application x:Class="CSDN01.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:local="clr-namespace:CSDN01"
             StartupUri="MainWindow.xaml">
    <Application.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="TemplateMyButton.xaml"/>
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Application.Resources>
</Application>
  1. 主窗口文件(MainWindow.xaml)
    属性UseLayoutRounding=“True”
    的设置是一个注意点(后续♦2处说明)
<Window x:Class="CSDN01.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:CSDN01"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Grid UseLayoutRounding="True">
        <Grid.RowDefinitions>
            <RowDefinition Height="*"/>
            <RowDefinition Height="3*"/>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>
        <local:MyButton Template="{StaticResource TemplateMyButton}" Grid.Column="1"/>
        <local:MyButton Template="{StaticResource TemplateMyButton}" Grid.Column="2"/>
        <local:MyButton Template="{StaticResource TemplateMyButton}" Grid.Column="3"/>
        <local:MyButton Template="{StaticResource TemplateMyButton}" Grid.Column="4"/>
    </Grid>
</Window>
  1. 上述步骤后,大体窗口的样子成型
    在这里插入图片描述

  2. 现在重写MyButton.cs文件添加DoubleAnimation动画
    属性FillBehavior
    的设置是一个注意点(后续♦3处说明)

using System;
using System.Windows.Media.Animation;
       /// <summary>
        /// 动画对象
        /// </summary>
        private DoubleAnimation myAnimation = new DoubleAnimation();

        /// <summary>
        /// 重写MouseEnter事件
        /// </summary>
        /// <param name="e"></param>
        protected override void OnMouseEnter(MouseEventArgs e)
        {
            base.OnMouseEnter(e);

            // 获取MyButton内部子控件
            Border bd1 = GetTemplateChild("bd1") as Border;
            Border bd2 = GetTemplateChild("bd2") as Border;

            // 设置属性
            myAnimation.From = 0;                             // 动画初始值
            myAnimation.To = bd1.ActualHeight;                // 动画终了值
            myAnimation.Duration = TimeSpan.FromSeconds(0.5); // 0.5秒内执行动画
            myAnimation.FillBehavior = FillBehavior.HoldEnd;  // 动画结束后,属性值保持终了状态

            // 在高度属性上执行动画
            bd2.BeginAnimation(HeightProperty, myAnimation);
        }

        /// <summary>
        /// 重写MouseLeave事件
        /// </summary>
        /// <param name="e"></param>
        protected override void OnMouseLeave(MouseEventArgs e)
        {
            base.OnMouseLeave(e);

            // 获取MyButton内部子控件
            Border bd1 = GetTemplateChild("bd1") as Border;
            Border bd2 = GetTemplateChild("bd2") as Border;

            // 添加并执行动画
            myAnimation.From = bd1.ActualHeight;              // 动画初始值
            myAnimation.To = 0;                               // 动画终了值
            myAnimation.Duration = TimeSpan.FromSeconds(0.5); // 0.5秒内执行动画
            myAnimation.FillBehavior = FillBehavior.Stop;     // 动画结束后,属性值还原为动画出师值

            // 在高度属性上执行动画
            bd2.BeginAnimation(HeightProperty, myAnimation);
        }
  1. 上述步骤后,整个按钮算是大体全部结束了,后续要添加依赖属性,这样可以设置自己想要的背景色,文字大小颜色等属性。
    在这里插入图片描述

  2. 依赖属性的设置(这里就只设置2个依赖属性做例子)
    依赖属性的代码有快捷键:输入propdp,然后按2次Tab键
    往MyButton.cs添加下面的代码
    PropertyMetadata里设置的是初期值

using System.Windows.Media;
        /// <summary>
        /// 上层背景色(变动层)
        /// </summary>
        public SolidColorBrush UpBackground
        {
            get { return (SolidColorBrush)GetValue(UpBackgroundProperty); }
            set { SetValue(UpBackgroundProperty, value); }
        }
        public static readonly DependencyProperty UpBackgroundProperty =
            DependencyProperty.Register("UpBackground", typeof(SolidColorBrush), typeof(MyButton), new PropertyMetadata(new SolidColorBrush(Colors.DarkCyan)));

        /// <summary>
        /// 动画执行时间属性(秒)
        /// </summary>
        public double AnimationSeconds
        {
            get { return (double)GetValue(AnimationSecondsProperty); }
            set { SetValue(AnimationSecondsProperty, value); }
        }
        public static readonly DependencyProperty AnimationSecondsProperty =
            DependencyProperty.Register("AnimationSeconds", typeof(double), typeof(MyButton), new PropertyMetadata(0.2));

对MyButton.cs文件再次进行修改

            // 修改前
            myAnimation.Duration = TimeSpan.FromSeconds(0.5); // 0.5秒内执行动画
            // 修改后
            myAnimation.Duration = TimeSpan.FromSeconds(AnimationSeconds); // 制定时间内执行动画(默认0.2秒)

对TemplateMyButton.xaml文件再次进行修改

            <!--变更前-->
            <Border x:Name="bd2" Background="DarkCyan" Height="0">
            <!--变更后-->
            <Border x:Name="bd2" Background="{TemplateBinding UpBackground}" Height="0">

也可以用Button固有的属性来设置MyButton的样式

            <!--变更前-->
            <TextBlock Text="按钮" FontSize="12" 
            <!--变更后-->
            <TextBlock Text="{TemplateBinding Content}"

当然要在MainWindow.xaml文件里设置Content的值

        <local:MyButton Template="{StaticResource TemplateMyButton}" Grid.Column="1" Content="首页"/>
        <local:MyButton Template="{StaticResource TemplateMyButton}" Grid.Column="2" Content="产品"/>
        <local:MyButton Template="{StaticResource TemplateMyButton}" Grid.Column="3" Content="技术"/>
        <local:MyButton Template="{StaticResource TemplateMyButton}" Grid.Column="4" Content="联系我们"/>

到此代码都结束了

在这里插入图片描述

补充上述3点我在做样式的注意点

  1. 先说注意点♦2:UseLayoutRounding=“True”
    做个小例子
        <Grid Grid.Row="2" Grid.Column="1">
            <Border Background="Black"/>
            <Border Background="White"/>
        </Grid>

上述在一个单元格里放至2个Border,白色覆盖在黑色上面,下面是没有UseLayoutRounding=“True”
的运行结果
在这里插入图片描述

有2根比较明显的底色黑线漏出来了,我这次给的MyButton例子是不会出现这个的,因为一个是隐藏的,一个是显示的。但是如果自己想做别的动画,需要多个重叠控件的话,就可能会出现这个现象。
至于为什么,我也说不出,给2张书上的说明:
在这里插入图片描述

在这里插入图片描述

引用的是【WPF编程宝典–使用C#2012和.NET 4.5 第4版】的64,65页。

  1. 再说注意点♦1:UseLayoutRounding=“False”
    举个小例子,去掉♦1处的:UseLayoutRounding=“False”
   <Grid UseLayoutRounding="True">
        <Grid.RowDefinitions>
            <RowDefinition Height="*"/>
            <RowDefinition Height="*"/>
            <RowDefinition Height="*"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="120"/>
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>
        <local:MyButton Template="{StaticResource TemplateMyButton}" Grid.Column="1" Content="首页" Padding="5"/>

为了防止黑线,我在最外面添加了UseLayoutRounding=“True”,如果MyButton被强制设置了宽度,又加上了Padding,MyButton的实际宽度编程110,我用慢动作看一下运行的实际动画:
在这里插入图片描述

文字在上下抖动,原因大概就是高度是小数,不停变化高度的时候,由于布局舍入导致居中的位置上下1像素浮动。这时候就要把布局舍入给关了。

  1. 再说注意点♦3:属性FillBehavior
    用一个别的按钮样式说明,没有FillBehavior属性,被拉伸后的效果
    拉伸前:
    在这里插入图片描述
    拉伸后:
    在这里插入图片描述

应该很好理解,原本高宽是考Grid自动生成的,所以控件实际的Hight和Width的值是NaN,所以动画我用的都是ActualHeight和ActualWidth,动画执行的时候,改变的是Hight和Width,把实际的高宽给固定了,所以拉伸后还保持原来的Hight和Width而没有跟随Grid的变化而变化,所以要在鼠标离开后,把NaN的值还给他。

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值