WPF动画启动,但显示太迟

本文关键字:显示 太迟 动画 启动 WPF | 更新日期: 2023-09-27 17:57:38

我正在使用.Net 4.0构建一些WPF控件。其中一个名为LoadingPane的控件是从ContentControl派生的自定义控件。此LoadingPane控件的唯一工作是在其IsLoading属性设置为true时,在其包含的内容上显示半透明层。我使用一些动画来进行淡入淡出,当IsLoading值更改时淡出。显示覆盖时,动画会旋转一个消隐圆。

到目前为止,一切都很好。这一切都做得很好。但我的问题是:当我将Loading属性设置为true时,动画不会直接显示。大约需要半秒钟。此时,淡入动画已经运行,因此不透明度在一步中有效地从0变为1。

这是我的动画代码:

<ControlTemplate.Triggers>
    <Trigger Property="IsLoading"
             Value="True">
        <Trigger.EnterActions>
            <RemoveStoryboard BeginStoryboardName="EndAnimateLoadingCanvas" />
            <BeginStoryboard Name="AnimateLoadingCanvas">
                <Storyboard FillBehavior="Stop">
                    <ObjectAnimationUsingKeyFrames BeginTime="00:00:00"
                                                   Storyboard.TargetName="MyViewBoxje"
                                                   Storyboard.TargetProperty="Visibility">
                        <DiscreteObjectKeyFrame Value="{x:Static Visibility.Visible}" />
                    </ObjectAnimationUsingKeyFrames>
                    <DoubleAnimation BeginTime="00:00:00"
                                     Duration="00:00:00.5"
                                     Storyboard.TargetName="MyViewBoxje"
                                     Storyboard.TargetProperty="Opacity"
                                     To="1" />
                    <DoubleAnimation BeginTime="00:00:00"
                                     Duration="00:00:02"
                                     Storyboard.TargetName="AnimatedRotateTransform"
                                     Storyboard.TargetProperty="Angle"
                                     From="360"
                                     To="0"
                                     RepeatBehavior="Forever" />
                </Storyboard>
            </BeginStoryboard>
        </Trigger.EnterActions>
        <Trigger.ExitActions>
            <RemoveStoryboard BeginStoryboardName="AnimateLoadingCanvas" />
            <BeginStoryboard Name="EndAnimateLoadingCanvas">
                <Storyboard FillBehavior="Stop">
                    <DoubleAnimation BeginTime="00:00:00"
                                     Duration="00:00:00.5"
                                     Storyboard.TargetName="MyViewBoxje"
                                     Storyboard.TargetProperty="Opacity"
                                     To="0" />
                    <ObjectAnimationUsingKeyFrames BeginTime="00:00:00.5"
                                                   Storyboard.TargetName="MyViewBoxje"
                                                   Storyboard.TargetProperty="Visibility">
                        <DiscreteObjectKeyFrame Value="{x:Static Visibility.Collapsed}" />
                    </ObjectAnimationUsingKeyFrames>
                </Storyboard>
            </BeginStoryboard>
        </Trigger.ExitActions>
    </Trigger>
</ControlTemplate.Triggers>

奇怪的是,当我在测试窗口中使用此控件,并重复单击"加载"复选框(并且在动画完成之前)时,淡入/淡出动画确实按我预期的方式工作。

有人能帮忙吗?提前感谢!

WPF动画启动,但显示太迟

如果不看代码的其余部分,很难确切地了解问题所在,但我猜这与动画属性的起始值有关。

我在WPF中实现了一个自定义控件,在视图框中有一个矩形,并使用问题中的触发器+故事板来查看效果。事实上,我的第一次尝试既没有消退,也没有消退。

我所做的是通过在动画中指定From值来解决这个问题,这样无论DP的原始值是什么,它们都能工作:

<DoubleAnimation BeginTime="00:00:00"
      Duration="00:00:00.5"
      Storyboard.TargetName="MyViewBoxje"
      Storyboard.TargetProperty="Opacity"
      From="0"
      To="1" />

请注意上面动画中的From="0"。结尾情节提要以相同的方式进行了修改,从1变为0。

为了完整起见,我还在ControlTemplate中的viewbox元素的定义中将不透明度设置为0


以下是相关部分的完整源代码。该控件是继承自control的标准WPF自定义控件。它有一个名为IsLoadingbool)的单一依赖属性,默认为false:

public bool IsLoading
{
    get { return (bool)GetValue(IsLoadingProperty); }
    set { SetValue(IsLoadingProperty, value); }
}
// Using a DependencyProperty as the backing store for IsLoading.  This enables animation, styling, binding, etc...
public static readonly DependencyProperty IsLoadingProperty =
        DependencyProperty.Register("IsLoading", typeof(bool), typeof(LoadingControl), new UIPropertyMetadata(false));

ControlTemplate-以{x:Type local:LoadingControl} 的样式在generic.xaml中定义

<ControlTemplate TargetType="{x:Type local:LoadingControl}">
    <ControlTemplate.Triggers>
        <Trigger Property="IsLoading" Value="True">
            <Trigger.EnterActions>
                <RemoveStoryboard BeginStoryboardName="EndAnimateLoadingCanvas" />
                <BeginStoryboard Name="AnimateLoadingCanvas">
                    <Storyboard FillBehavior="Stop">
                        <ObjectAnimationUsingKeyFrames BeginTime="00:00:00"
                                   Storyboard.TargetName="MyViewBoxje"
                                   Storyboard.TargetProperty="Visibility">
                            <DiscreteObjectKeyFrame Value="{x:Static Visibility.Visible}" />
                        </ObjectAnimationUsingKeyFrames>
                        <DoubleAnimation BeginTime="00:00:00"
                                     Duration="00:00:00.5"
                                     Storyboard.TargetName="MyViewBoxje"
                                     Storyboard.TargetProperty="Opacity"
                                     From="0"
                                     To="1" />
                        <DoubleAnimation BeginTime="00:00:00"
                                         Duration="00:00:02"
                                         Storyboard.TargetName="AnimatedRotateTransform"
                                         Storyboard.TargetProperty="Angle"
                                         From="360"
                                         To="0"
                                         RepeatBehavior="Forever" />
                    </Storyboard>
                </BeginStoryboard>
            </Trigger.EnterActions>
            <Trigger.ExitActions>
                <RemoveStoryboard BeginStoryboardName="AnimateLoadingCanvas" />
                <BeginStoryboard Name="EndAnimateLoadingCanvas">
                    <Storyboard FillBehavior="Stop">
                        <DoubleAnimation BeginTime="00:00:00"
                                         Duration="00:00:00.5"
                                         Storyboard.TargetName="MyViewBoxje"
                                         Storyboard.TargetProperty="Opacity"
                                         From="1"
                                        To="0" />
                        <ObjectAnimationUsingKeyFrames BeginTime="00:00:00.5"
                                   Storyboard.TargetName="MyViewBoxje"
                                   Storyboard.TargetProperty="Visibility">
                            <DiscreteObjectKeyFrame Value="{x:Static Visibility.Collapsed}" />
                        </ObjectAnimationUsingKeyFrames>
                    </Storyboard>
                </BeginStoryboard>
            </Trigger.ExitActions>
        </Trigger>
    </ControlTemplate.Triggers>
    <Border Background="{TemplateBinding Background}"
            BorderBrush="{TemplateBinding BorderBrush}"
            BorderThickness="{TemplateBinding BorderThickness}">
        <Viewbox x:Name="MyViewBoxje" Opacity="0">
            <!-- BG with 0x50 alpha so that it's translucent event at 100% visibility -->
            <Grid Width="100" Height="100" Background="#50000000">
                <Rectangle Width="70" Height="20" Fill="Green" Stroke="Black" StrokeThickness="2" RenderTransformOrigin="0.5,0.5">
                    <Rectangle.RenderTransform>
                        <RotateTransform Angle="360" x:Name="AnimatedRotateTransform" />
                    </Rectangle.RenderTransform>
                </Rectangle>
            </Grid>
        </Viewbox>
    </Border>
</ControlTemplate>

我在我的主窗口中这样使用:

<Grid x:Name="LayoutRoot">
    <!-- All other stuff here ... -->
    <my:LoadingControl IsLoading="{Binding IsLoading}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" />
</Grid>

为了测试它,我有一个ViewModel,它的属性IsLoading被设置为主窗口的DataContext。在ViewModel的构造函数中,我将IsLoading设置为true,然后启动一个计时器,每隔5秒切换一次属性值:

public MainWindowViewModel()
{
    IsLoading = true;
    DispatcherTimer t = new DispatcherTimer();
    t.Interval = TimeSpan.FromSeconds(5);
    t.Tick += (s, e) => IsLoading = !IsLoading;
    t.Start();
}

在阅读了Isak的答案后,我终于明白了。很抱歉,他的回答对我没有帮助,但他让我朝着正确的方向前进。

第一个淡入似乎不起作用的原因是,在执行淡入动画的整个过程中,我的包含视框的可见性设置为塌陷。这是由ObjectAnimationUsingKeyFrames引起的:

    DiscreteObjectKeyFrame Value="{x:Static Visibility.Visible}" />
</ObjectAnimationUsingKeyFrames>

没有指定持续时间,指定的单个关键帧动画设置得太晚。

添加

Duration="00:00:00"

解决了我的问题。

感谢大家的帮助!