动画生命周期

WPF动画生命周期

动画会暂时覆盖属性的值,但不会自动修改属性的基础值。

① 动画开始前:属性有自己的基础值

复制代码
<Button x:Name="btn"
        Width="100"
        Height="50"
        Content="按钮"/>

此时:

项目 值
Width 的基础值 100
界面实际使用的宽度 100

这里的"基础值",可以先理解为:不考虑动画时,这个属性应该使用的值。 本例中,就是 XAML 设置的 100。


② 动画运行中:动画值暂时覆盖基础值

启动一个让宽度从 100 变成 300 的动画:

复制代码
DoubleAnimation animation = new DoubleAnimation
{
    From = 100,
    To = 300,
    Duration = TimeSpan.FromSeconds(2)
};

btn.BeginAnimation(
    FrameworkElement.WidthProperty,
    animation);

假设动画执行到一半:

项目 值
Width 的基础值 仍然是 100
当前动画值 约 200
界面实际使用的宽度 约 200

按钮变宽了,但原本设置的 100 并没有被动画改成 200。

你可以把它想成:基础值还在下面,动画临时提供了一个优先使用的值。


③ 动画播放完:默认继续保持终点值

2 秒后,按钮宽度变为 300。

你可能以为:

动画已经结束,那么它应该不再影响按钮了。

但是默认情况下,动画的:

复制代码
FillBehavior = FillBehavior.HoldEnd;

HoldEnd 表示:播放完成后,继续保持动画的最终效果。

因此:

项目 值
Width 的基础值 100
动画保持的值 300
界面实际使用的宽度 300

这里要区分两件事:

  • 播放完成:属性不再从 100 逐渐变到 300。
  • 停止影响属性:属性不再使用动画提供的值。

播放完成,不代表动画已经停止影响属性。

这种现象描述为动画结束后仍保持作用。更严格地说,默认情况下此时进入的是填充阶段(Filling),已经不是正在变化的活动阶段(Active)。

④ 为什么动画结束后,重新赋值"没有效果"?

假设动画播放结束后执行:

复制代码
btn.Width = 150;

按钮可能仍然显示为 300。

不是这句代码没执行,而是现在变成了:

项目 值
Width 的基础值 已经改成 150
动画保持的值 仍然是 300
界面实际使用的宽度 300

基础值改了,但动画值仍然优先。

这就是这一节最容易遇到的问题。

⑤ 解决办法一:结束后恢复基础值

设置:

复制代码
animation.FillBehavior = FillBehavior.Stop;

完整写法:

复制代码
DoubleAnimation animation = new DoubleAnimation
{
    From = 100,
    To = 300,
    Duration = TimeSpan.FromSeconds(2),
    FillBehavior = FillBehavior.Stop
};

btn.BeginAnimation(
    FrameworkElement.WidthProperty,
    animation);

假设基础值一直是 100,效果就是:

阶段 实际宽度
开始前 100
播放中 从 100 逐渐变到 300
播放完成后 恢复为 100

注意:恢复的是当时的基础值。 如果你在动画过程中把基础值改成了 150,那么动画结束后就会显示 150。

另外,FillBehavior.Stop 是让动画结束后不再提供属性值,不等同于把动画对象删除或立刻回收内存。

⑥ 解决办法二:主动移除属性上的动画

通过下面的代码,移除宽度属性上的动画影响:

复制代码
btn.BeginAnimation(
    FrameworkElement.WidthProperty,
    null);

这里的 null 表示不再给这个属性应用动画。

例如:

复制代码
// 移除动画,恢复使用基础值
btn.BeginAnimation(
    FrameworkElement.WidthProperty,
    null);

// 正常设置宽度
btn.Width = 150;

这次按钮就会显示为 150。

注意:它移除的是 Width 属性上的动画,不是按钮上的所有动画。

⑦ 解决办法三:播放结束后保留最终宽度,同时移除动画

实际开发中,你可能希望:

按钮变宽到 300 后就保持 300,并且以后还能正常修改 Width。

可以在 Completed 事件中处理:

复制代码
private void btn_Click(object sender, RoutedEventArgs e)
{
    DoubleAnimation animation = new DoubleAnimation
    {
        From = 100,
        To = 300,
        Duration = TimeSpan.FromSeconds(2)
    };

    animation.Completed += (s, args) =>
    {
        // 1. 先记住动画作用下的当前宽度
        double currentWidth = btn.Width;

        // 2. 移除动画
        btn.BeginAnimation(
            FrameworkElement.WidthProperty,
            null);

        // 3. 把刚才的宽度设置为基础值
        btn.Width = currentWidth;
    };

    btn.BeginAnimation(
        FrameworkElement.WidthProperty,
        animation);
}

顺序很重要:

  1. 先记录当前宽度 300。
  2. 再移除动画。
  3. 再赋值,让基础值也变为 300。

这样,动画移除后,按钮依然保持 300。以后再执行:

复制代码
btn.Width = 150;

就可以正常变成 150。

这里的 Completed 表示动画完成了活动期,并不表示动画已经自动移除 。上述例子使用默认的 HoldEnd,所以能在事件中读取到保持的最终值。

⑧ 书里另外两种方式,应该怎样理解?

书中还提到"回到原值"的动画,以及:

复制代码
animation.AutoReverse = true;

AutoReverse 的效果是:

从 100 变到 300,再从 300 变回 100。

但要注意:反向播放回到 100,不代表动画已被移除。 如果仍使用默认的 HoldEnd,它还可能保持反向播放结束时的值。

因此,当你的目的是"动画结束后,让普通赋值恢复正常",最直接的办法是:

你的需求 做法
播放后恢复基础值 FillBehavior.Stop
立即取消动画影响 BeginAnimation(属性, null)
播放后保留最终值,并恢复正常赋值 Completed 中记录当前值、移除动画、再赋值

记住这一节的核心关系:基础值一直存在;动画可以覆盖它;动画播放完毕后,是否继续覆盖,由结束行为和移除操作决定。

相关推荐
huaqianzkh10 小时前
WinForm C#:循环修改控件UI闪烁卡顿?SuspendLayout / ResumeLayout 与 BeginUpdate 优化
开发语言·ui·c#
用户3721574261351 天前
C# 实现 Excel 单元格保护:锁定公式并允许编辑指定区域
c#·ai编程·编程语言
公子小六1 天前
基于.NET的Windows窗体编程之WinForms数据持久化(1)
windows·microsoft·c#·.net·winforms
三雷科技1 天前
第6章第4节:自定义分词器
开发语言·c#
zhanghaha13141 天前
C# 系列教程_12 字符串 string 保姆级系统教程(面向新手,大量示例 + 原理讲解)
开发语言·数据库·c#
Coder_Shenshen2 天前
罗克韦尔ACD工程归档离线变量树解析
算法·c#
samble2 天前
从零搭建半导体设备上位机(九):拖拽与交互,Adorner、DragDrop 与晶圆拖放信息
wpf·拖拽·adorner
寂寞的船2 天前
【Unity知识速递】第10期 Unity GC 零分配实战:从 UnityEngine.Pool 到手动 GC 控制的全链路优化
jvm·unity·c#·游戏引擎·lucene
1-1=02 天前
C# 数据结构和特殊类型
数据结构·c#·.net·.netcore
郝学胜-神的一滴3 天前
AI 编程智能体 27:吃透MCP通信机制
人工智能·python·langchain·wpf·numpy·scikit-learn