如何仅在 DependencyProperty 更改(不是默认值)时触发动画?

How do I trigger Animation on DependencyProperty change only (not default value)?

似乎我总是被最简单的事情难倒...
我有一个带有自定义 bool DependencyProperty 'GpoModule' 的 UserControl。

默认设置为 false。

当 'GpoModule' = true 时触发 FadeIn 动画,当 'GpoModule' = false.

时触发 FadeOut 动画 我的问题是,加载控件时会触发 FadeOut 动画,我希望仅在更改 属性 时触发动画(或者至少在加载时不在默认值上)。我怀疑我可能必须定义某种事件触发器,但我希望避免这种情况。

依赖属性:

    public bool GpoModule {
        get { return (bool)GetValue(GpoModuleProperty); }
        set { SetValue(GpoModuleProperty, value); }
    }
    public static readonly DependencyProperty GpoModuleProperty =
        DependencyProperty.Register("GpoModule", typeof(bool),
            typeof(ModuleIndicator), new UIPropertyMetadata(false));

动画:

 <UserControl.Resources>
    <Duration x:Key="D">0:0:0.6</Duration>
    <Storyboard x:Key="SbFadeOut">
        <DoubleAnimation Storyboard.TargetProperty="(FrameworkElement.Width)"
                         From="38" To="0"
                         Duration="{StaticResource D}">
            <DoubleAnimation.EasingFunction>
                <QuadraticEase EasingMode="EaseInOut" />
            </DoubleAnimation.EasingFunction>
        </DoubleAnimation>
        <DoubleAnimation Storyboard.TargetProperty="(FrameworkElement.Height)"
                         From="38"
                         To="0"
                         Duration="{StaticResource D}">
            <DoubleAnimation.EasingFunction>
                <QuadraticEase EasingMode="EaseInOut" />
            </DoubleAnimation.EasingFunction>
        </DoubleAnimation>
        <DoubleAnimation Storyboard.TargetProperty="(UIElement.Opacity)"
                         From="1"
                         To="0"
                         Duration="{StaticResource D}">
            <DoubleAnimation.EasingFunction>
                <QuadraticEase EasingMode="EaseInOut" />
            </DoubleAnimation.EasingFunction>
        </DoubleAnimation>
    </Storyboard>
    <Storyboard x:Key="SbFadeIn">
        <DoubleAnimation Storyboard.TargetProperty="(FrameworkElement.Width)"
                         From="0" To="38"
                         Duration="{StaticResource D}">
            <DoubleAnimation.EasingFunction>
                <QuadraticEase EasingMode="EaseInOut" />
            </DoubleAnimation.EasingFunction>
        </DoubleAnimation>
        <DoubleAnimation Storyboard.TargetProperty="(FrameworkElement.Height)"
                         From="0"
                         To="38"
                         Duration="{StaticResource D}">
            <DoubleAnimation.EasingFunction>
                <QuadraticEase EasingMode="EaseInOut" />
            </DoubleAnimation.EasingFunction>
        </DoubleAnimation>
        <DoubleAnimation Storyboard.TargetProperty="(UIElement.Opacity)"
                         From="0"
                         To="1"
                         Duration="{StaticResource D}">
            <DoubleAnimation.EasingFunction>
                <QuadraticEase EasingMode="EaseInOut" />
            </DoubleAnimation.EasingFunction>
        </DoubleAnimation>
    </Storyboard>        
</UserControl.Resources>

动画元素:

   <Viewbox x:Name="GpoIndicator"
            VerticalAlignment="Center" HorizontalAlignment="Center"
                              Stretch="Fill" Width="0" Height="0">
        <Grid Height="38" Width="38">
            <Ellipse Height="38"
                    Fill="{DynamicResource App.Primary.Light}"
                    Stroke="{DynamicResource App.Border}" />
            <TextBlock Width="38"
                    Height="38"
                    Margin="0"
                    HorizontalAlignment="Left"
                    VerticalAlignment="Top"
                    FontFamily="{DynamicResource AppFont}"
                    FontSize="30"
                    FontWeight="Bold"
                    Foreground="{DynamicResource ResourceKey=App.Accent.Secondary}"
                    Padding="0,2,0,0"
                    Text="G"
                    TextAlignment="Center"
                    TextWrapping="Wrap"/>
        </Grid>
        <Viewbox.Style>
            <Style TargetType="Viewbox">
                <Style.Triggers>
                    <DataTrigger Binding="{Binding GpoModule, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:ModuleIndicator}}}"
                             Value="True">
                        <DataTrigger.EnterActions>
                            <BeginStoryboard Name="GpoIn">
                                <Storyboard>
                                    <StaticResource ResourceKey="SbFadeIn" />
                                </Storyboard>
                            </BeginStoryboard>
                        </DataTrigger.EnterActions>
                        <DataTrigger.ExitActions>
                            <RemoveStoryboard BeginStoryboardName="GpoIn" />
                        </DataTrigger.ExitActions>                            
                    </DataTrigger>
                    <DataTrigger Binding="{Binding GpoModule, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:ModuleIndicator}}}"
                                 Value="False">
                        <DataTrigger.EnterActions>
                            <BeginStoryboard Name="GpoOut">
                                <Storyboard>
                                    <StaticResource ResourceKey="SbFadeOut" />
                                </Storyboard>
                            </BeginStoryboard>
                        </DataTrigger.EnterActions>
                        <DataTrigger.ExitActions>
                            <RemoveStoryboard BeginStoryboardName="GpoOut" />
                        </DataTrigger.ExitActions>
                    </DataTrigger>                       
                </Style.Triggers>
            </Style>
        </Viewbox.Style>
    </Viewbox>

谢谢!

编辑:我应该提到还有另外两个 'Indicator' 对象/子对象 - 'RedesignIndicator' 和 'NovellIndicator'。每个都有各自的 bool DependencyProperties。
在提出问题之前,我尝试使用多个条件。我的第一次尝试是将条件设置为 'IsLoaded' 但没有效果。
我的第二次尝试是使用另一个 Dependency 属性 'AnimationLocked',默认设置为 true,并尝试了两种变体。
一:在 属性 已更改的回调上将 AnimationLocked 设置为 false(这具有在其他子对象之一和
上触发 FadeOut 动画的副作用 二:在 UserControl.Loaded 事件(没有效果)

上将 AnimationLocked 设置为 false

您可以使用多数据触发器:

<Viewbox.Style>
    <Style TargetType="Viewbox">
        <Style.Triggers>
            <MultiDataTrigger>
                <MultiDataTrigger.Conditions>
                    <Condition Binding="{Binding IsLoaded}" Value="True" />
                    <Condition Binding="{Binding GpoModule}" Value="True" />
                </MultiDataTrigger.Conditions>
                <MultiDataTrigger.EnterAction>
                    ... fade in animation
                </MultiDataTrigger.EnterAction>
            </MultiDataTrigger>
            <MultiDataTrigger>
                <MultiDataTrigger.Conditions>
                    <Condition Binding="{Binding IsLoaded}" Value="True" />
                    <Condition Binding="{Binding GpoModule}" Value="False" />
                </MultiDataTrigger.Conditions>
                <MultiDataTrigger.EnterAction>
                    ... fade out animation
                </MultiDataTrigger.EnterAction>
            </MultiDataTrigger>
        </Style.Triggers>
    </Style>
</Viewbox.Style>

IsLoaded 是另一个依赖项 属性,它在控件 Loaded 事件处理程序中设置为 true

这样动画只会在IsLoaded == true时出现。

P.S.: 不确定是否必须在控件 Unloaded 事件处理程序中设置 IsLoaded = false

我最终确定的解决方案是为我控件上的三个子项 'Module Indicators' 中的每一个添加一个 'AnimationLocked' bool DependencyProperty。 'AnimationLocked' 的默认值为 true 并在 'Module' bool DependencyProperty 回调中设置为 false,这确保动画被阻止,直到 属性 被第一次用户交互手动更改。我希望有一些内置的方式可以防止 'triggering' 触发默认值。这看起来很不雅,我仍然愿意接受其他建议。

UserControl 代码隐藏:

 public bool GpoAnimationLocked
    {
        get { return (bool)GetValue(GpoAnimationLockedProperty); }
        set { SetValue(GpoAnimationLockedProperty, value); }
    }
    public static readonly DependencyProperty GpoAnimationLockedProperty =
        DependencyProperty.Register("GpoAnimationLocked", typeof(bool),
            typeof(ModuleIndicator), new UIPropertyMetadata(true));

    public bool GpoModule {
        get { return (bool)GetValue(GpoModuleProperty); }
        set { SetValue(GpoModuleProperty, value); }
    }
    public static readonly DependencyProperty GpoModuleProperty =
        DependencyProperty.Register("GpoModule", typeof(bool),
            typeof(ModuleIndicator), new UIPropertyMetadata(false, GpoSelectionPropertyChanged));

    protected virtual void GpoSelectionChanged(DependencyPropertyChangedEventArgs e) {
        GpoAnimationLocked = false;
    }
    private static void GpoSelectionPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e) {
        ((ModuleIndicator)sender).GpoSelectionChanged(e);
    }

UserControl xaml(动画的子 'indicator':

    <Viewbox x:Name="GpoIndicator"
             VerticalAlignment="Center"
             HorizontalAlignment="Center"
             Stretch="Fill"
             Width="0"
             Height="0">
        <Grid Height="38"
              Width="38">
            <Ellipse Height="38"
                     Fill="{DynamicResource App.Primary.Light}"
                     Stroke="{DynamicResource App.Border}" />
            <TextBlock Width="38"
                       Height="38"
                       Margin="0"
                       HorizontalAlignment="Left"
                       VerticalAlignment="Top"
                       FontFamily="{DynamicResource AppFont}"
                       FontSize="30"
                       FontWeight="Bold"
                       Foreground="{DynamicResource ResourceKey=App.Accent.Secondary}"
                       Padding="0,2,0,0"
                       Text="G"
                       TextAlignment="Center"
                       TextWrapping="Wrap" />
        </Grid>
        <Viewbox.Style>
            <Style TargetType="Viewbox">
                <Style.Triggers>
                    <DataTrigger Binding="{Binding GpoModule, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:ModuleIndicator}}}"
                                 Value="True">
                        <DataTrigger.EnterActions>
                            <BeginStoryboard Name="In">
                                <Storyboard>
                                    <StaticResource ResourceKey="SbFadeIn" />
                                </Storyboard>
                            </BeginStoryboard>
                        </DataTrigger.EnterActions>
                        <DataTrigger.ExitActions>
                            <RemoveStoryboard BeginStoryboardName="In" />
                        </DataTrigger.ExitActions>
                    </DataTrigger>
                    <MultiDataTrigger>
                        <MultiDataTrigger.Conditions>
                            <Condition Binding="{Binding GpoAnimationLocked, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:ModuleIndicator}}}"
                                       Value="False" />
                            <Condition Binding="{Binding GpoModule, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:ModuleIndicator}}}"
                                       Value="False" />
                        </MultiDataTrigger.Conditions>
                        <MultiDataTrigger.EnterActions>
                            <BeginStoryboard Name="Out">
                                <StaticResource ResourceKey="SbFadeOut" />
                            </BeginStoryboard>
                        </MultiDataTrigger.EnterActions>
                        <MultiDataTrigger.ExitActions>
                            <RemoveStoryboard BeginStoryboardName="Out" />
                        </MultiDataTrigger.ExitActions>
                    </MultiDataTrigger>
                </Style.Triggers>
            </Style>
        </Viewbox.Style>
    </Viewbox>