天天看点

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

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

    • 效果展示
    • 原理
    • 下面为其中一个样式的代码
    • 到此代码都结束了
    • 补充上述3点我在做样式的注意点

效果展示

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. 上述步骤后,大体窗口的样子成型
    WPF自定义控件-按钮-DoubleAnimation动画
  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. 上述步骤后,整个按钮算是大体全部结束了,后续要添加依赖属性,这样可以设置自己想要的背景色,文字大小颜色等属性。
    WPF自定义控件-按钮-DoubleAnimation动画
  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="联系我们"/>
                

到此代码都结束了

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

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

  1. 先说注意点♦2:UseLayoutRounding=“True”

    做个小例子

<Grid Grid.Row="2" Grid.Column="1">
            <Border Background="Black"/>
            <Border Background="White"/>
        </Grid>
                

上述在一个单元格里放至2个Border,白色覆盖在黑色上面,下面是没有UseLayoutRounding=“True”

的运行结果

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

有2根比较明显的底色黑线漏出来了,我这次给的MyButton例子是不会出现这个的,因为一个是隐藏的,一个是显示的。但是如果自己想做别的动画,需要多个重叠控件的话,就可能会出现这个现象。

至于为什么,我也说不出,给2张书上的说明:

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

引用的是【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,我用慢动作看一下运行的实际动画:

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

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

  1. 再说注意点♦3:属性FillBehavior

    用一个别的按钮样式说明,没有FillBehavior属性,被拉伸后的效果

    拉伸前:

    WPF自定义控件-按钮-DoubleAnimation动画
    拉伸后:
    WPF自定义控件-按钮-DoubleAnimation动画

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

继续阅读