动画生命周期

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 中记录当前值、移除动画、再赋值

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

相关推荐
波力海苔夹心脆6755 小时前
C# StringBuilder 详解:字符串拼接为什么总说要用它?
开发语言·笔记·c#·.net·字符串·stringbuilder·入门教程
淡海水5 小时前
06-03-源码-时间分片与调度策略
unity·c#·gc
丑梨数据6 小时前
法律合同知识库怎么搭:从图片压缩到私有化 RAG 的工程笔记
开发语言·笔记·c#
1-1=06 小时前
.NET Core 输出日志
c#·.net·.netcore
曹牧7 小时前
C#里引用ActiveX控件
开发语言·c#
宇卿.7 小时前
C#委托详解
开发语言·c#
努力,努力再努力7 小时前
零依赖 C# Office 库到底怎么用——四个场景讲透 Wisdom.Office
c#
2501_9307077811 小时前
使用C#代码在 PDF 文档中创建交互操作
pdf·c#·交互
zimoyin20 小时前
在 C# 里嵌入 JavaScript:用 Jint 做可热更新的自动化脚本(从入门到踩坑)
javascript·c#·自动化