首页 文章

WPF动画开始但显示太晚了

提问于
浏览
8

我正在使用.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>

奇怪的是,当我在测试窗口中使用此控件时,我反复单击“加载”复选框(并在动画完成之前),然后淡入/淡出动画确实可以正常工作 .

有人可以帮忙吗? Thanx提前!

2 回答

  • 0

    如果没有看到剩下的代码,很难确切地看到问题是什么,但我的猜测是它与动画属性的起始值有关 .

    我在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>
    

    为了测试它,我有一个带有属性 IsLoading 的ViewModel,它被设置为主窗口的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();
    }
    
  • 3

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

    第一次淡入似乎不起作用的原因是因为我的包含视图框的可见性设置为在整个淡入动画执行时折叠 . 这是由ObjectAnimationUsingKeyFrames引起的:

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

    没有指定持续时间,指定的单个关键帧动画太晚了 .

    添加

    Duration="00:00:00"
    

    解决了我的问题 .

    谢谢大家的帮助!

相关问题