UWP:共享故事板

Mar*_*ins 1 xaml uwp windows-10-universal uwp-xaml

在 WPF 中,我可以创建一个故事板作为页面/控件资源,然后多次将其用于该页面(或父控件)内的控件...

    <Grid Background="{DynamicResource CorporateLogoBackgroundBrush}" views:MainWindowHelper.DragWindowOnMouseDown="True">

        <Grid.Resources>

            <Storyboard x:Key="FlashRedBackgroundStoryboard">
                <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Background" Duration="0:0:1" RepeatBehavior="Forever">
                    <ObjectAnimationUsingKeyFrames.KeyFrames>
                        <DiscreteObjectKeyFrame KeyTime="0:0:0.5">
                            <DiscreteObjectKeyFrame.Value>
                                <SolidColorBrush Color="Red" />
                            </DiscreteObjectKeyFrame.Value>
                        </DiscreteObjectKeyFrame>
                    </ObjectAnimationUsingKeyFrames.KeyFrames>
                </ObjectAnimationUsingKeyFrames>
            </Storyboard>

        </Grid.Resources>

        <Border Grid.Column="4" BorderThickness="0">

            <Border.Style>
                <Style TargetType="{x:Type Border}">
                    <Style.Triggers>
                        <DataTrigger Binding="{Binding Path=IsNetworkConnectivityOk}" Value="false">
                            <DataTrigger.EnterActions>
                                <BeginStoryboard Name="FlashRedBackgroundStoryboard">
                                    <StaticResource  ResourceKey="FlashRedBackgroundStoryboard" />
                                </BeginStoryboard>
                            </DataTrigger.EnterActions>
                            <DataTrigger.ExitActions>
                                <StopStoryboard BeginStoryboardName="FlashRedBackgroundStoryboard"></StopStoryboard>
                            </DataTrigger.ExitActions>
                        </DataTrigger>
                    </Style.Triggers>
                </Style>
            </Border.Style>

            <TextBlock Text="LAN/WLAN" />

        </Border>

        <Border Grid.Column="5" BorderThickness="0">

            <Border.Style>
                <Style TargetType="{x:Type Border}">
                    <Setter Property="Background" Value="{DynamicResource CorporateLogoBackgroundBrush}" />
                    <Style.Triggers>
                        <DataTrigger Binding="{Binding Path=SynchronisationClientService.IsActive}" Value="true">
                            <Setter Property="Background" Value="Green" />
                        </DataTrigger>
                        <DataTrigger Binding="{Binding Path=SynchronisationClientService.IsRunning}" Value="false">
                            <DataTrigger.EnterActions>
                                <BeginStoryboard Name="FlashRedBackgroundStoryboard">
                                    <StaticResource  ResourceKey="FlashRedBackgroundStoryboard" />
                                </BeginStoryboard>
                            </DataTrigger.EnterActions>
                            <DataTrigger.ExitActions>
                                <StopStoryboard BeginStoryboardName="FlashRedBackgroundStoryboard"></StopStoryboard>
                            </DataTrigger.ExitActions>
                        </DataTrigger>
                    </Style.Triggers>
                </Style>
            </Border.Style>

            <TextBlock Text="SYNC" />

        </Border>

    </Grid>
Run Code Online (Sandbox Code Playgroud)

但是,我找不到在 UWP 中执行相同操作的方法。在 WPF 中使用 DataTrigger 和 BeginStoryboard 似乎会自动关联 Storyboard.TargetName,但在 UWP 中,我必须使用交互包,然后尝试以相同的方式将故事板与控件关联,因为故事板未与控件关联。 ..

<Grid Background="{StaticResource CorporateBackgroundBrush}">

    <Grid.Resources>

        <Storyboard x:Key="FlashRedBackgroundStoryboard">
            <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Background" Duration="0:0:1" RepeatBehavior="Forever">
                <ObjectAnimationUsingKeyFrames.KeyFrames>
                    <DiscreteObjectKeyFrame KeyTime="0:0:0.5">
                        <DiscreteObjectKeyFrame.Value>
                            <SolidColorBrush Color="Red" />
                        </DiscreteObjectKeyFrame.Value>
                    </DiscreteObjectKeyFrame>
                </ObjectAnimationUsingKeyFrames.KeyFrames>
            </ObjectAnimationUsingKeyFrames>
        </Storyboard>


    </Grid.Resources>


    <Border Grid.Column="4" BorderThickness="0">

        <interactivity:Interaction.Behaviors>
            <interactionsCore:DataTriggerBehavior Binding="{Binding IsNetworkConnectivityOk}" ComparisonCondition="Equal" Value="false">
                <media:ControlStoryboardAction Storyboard="{StaticResource FlashRedBackgroundStoryboard}" />
            </interactionsCore:DataTriggerBehavior>
        </interactivity:Interaction.Behaviors>

        <TextBlock Text="LAN/WLAN" />

    </Border>

    <Border Grid.Column="5" BorderThickness="0">

        <interactivity:Interaction.Behaviors>
            <interactionsCore:DataTriggerBehavior Binding="{Binding IsNetworkConnectivityOk}" ComparisonCondition="Equal" Value="false">
                <media:ControlStoryboardAction Storyboard="{StaticResource FlashRedBackgroundStoryboard}" />
            </interactionsCore:DataTriggerBehavior>
        </interactivity:Interaction.Behaviors>

        <TextBlock Text="SYNC" />

    </Border>

</Grid>
Run Code Online (Sandbox Code Playgroud)

我能找到在 UWP 中执行此操作的唯一方法是拥有多个相同的故事板,每个故事板都有对其将影响的控件的引用。

<Grid Background="{StaticResource CorporateBackgroundBrush}">

    <Grid.Resources>

        <Storyboard x:Name="NetworkConnectivityStoryboard">
            <ObjectAnimationUsingKeyFrames Storyboard.TargetName="NetworkConnectivityBorder" Storyboard.TargetProperty="Background" Duration="0:0:1" RepeatBehavior="Forever">
                <ObjectAnimationUsingKeyFrames.KeyFrames>
                    <DiscreteObjectKeyFrame KeyTime="0:0:0.5">
                        <DiscreteObjectKeyFrame.Value>
                            <SolidColorBrush Color="Red" />
                        </DiscreteObjectKeyFrame.Value>
                    </DiscreteObjectKeyFrame>
                </ObjectAnimationUsingKeyFrames.KeyFrames>
            </ObjectAnimationUsingKeyFrames>
        </Storyboard>

        <Storyboard x:Name="SynchronisationStatusStoryboard">
            <ObjectAnimationUsingKeyFrames Storyboard.TargetName="SynchronisationStatusBorder" Storyboard.TargetProperty="Background" Duration="0:0:1" RepeatBehavior="Forever">
                <ObjectAnimationUsingKeyFrames.KeyFrames>
                    <DiscreteObjectKeyFrame KeyTime="0:0:0.5">
                        <DiscreteObjectKeyFrame.Value>
                            <SolidColorBrush Color="Red" />
                        </DiscreteObjectKeyFrame.Value>
                    </DiscreteObjectKeyFrame>
                </ObjectAnimationUsingKeyFrames.KeyFrames>
            </ObjectAnimationUsingKeyFrames>
        </Storyboard>

    </Grid.Resources>


    <Border x:Name="NetworkConnectivityBorder" Grid.Column="4" BorderThickness="0">

        <interactivity:Interaction.Behaviors>
            <interactionsCore:DataTriggerBehavior Binding="{Binding IsNetworkConnectivityOk}" ComparisonCondition="Equal" Value="false">
                <media:ControlStoryboardAction Storyboard="{StaticResource NetworkConnectivityStoryboard}" />
            </interactionsCore:DataTriggerBehavior>
        </interactivity:Interaction.Behaviors>

        <TextBlock Text="LAN/WLAN" />

    </Border>

    <Border x:Name="SynchronisationStatusBorder" Grid.Column="5" BorderThickness="0">

        <interactivity:Interaction.Behaviors>
            <interactionsCore:DataTriggerBehavior Binding="{Binding IsSynchronisingk}" ComparisonCondition="Equal" Value="false">
                <media:ControlStoryboardAction Storyboard="{StaticResource SynchronisationStatusStoryboard}" />
            </interactionsCore:DataTriggerBehavior>
        </interactivity:Interaction.Behaviors>

        <TextBlock Text="SYNC" />

    </Border>

</Grid>
Run Code Online (Sandbox Code Playgroud)

有没有一种方法可以共享故事板,以便它可以针对多个控件使用(在这个特定实例中,它是一个状态栏,当关联的视图模型属性具有特定值时,某些元素需要闪烁。我不敢相信我实际上必须创建多个重复的对象才能实现此目的。

谢谢。

Jus*_* XL 5

在 UWP 中,一次Storyboard只能为一个动画制作动画。 Target因此从技术上讲,您可以手动停止(即Storyboard.Stop)它,更改其Target属性并再次启动动画(即Storyboard.Begin)。

但是如果您想同时为两个元素设置动画该怎么办?那么这个方法就行不通了。Storyboard这就是为什么为每个要设置动画的元素创建一个新元素是有意义的。


下面是使用附加属性而不是静态 XAML 的解决方案。需要编写一些代码,但是一旦设置完毕,它就非常易于使用(即一行 XAML 代码)并且几乎适用于任何元素。

这个想法是创建一个bool名为 的属性ShowFlashBackground,该属性可以附加到具有Background属性(例如BorderGrid或 any )的元素Control

然后,每当此属性设置为(例如由其true触发)时,调用扩展方法来启动彩色动画。IsNetworkConnectivityOkFlashElementBackground

代码

public static class Util
{
    public static void SetShowFlashBackground(DependencyObject element, bool value)
    {
        element.SetValue(ShowFlashBackgroundProperty, value);
    }
    public static bool GetShowFlashBackground(DependencyObject element)
    {
        return (bool)element.GetValue(ShowFlashBackgroundProperty);
    }
    public static readonly DependencyProperty ShowFlashBackgroundProperty = DependencyProperty.RegisterAttached(
        "ShowFlashBackground", typeof(bool), typeof(Util), new PropertyMetadata(true, (s, e) =>
        {
            if ((bool)e.NewValue)
            {
                var self = (FrameworkElement)s;
                self.FlashElementBackground();

                // Rest the value back to false so the callback can be triggered again.
                SetShowFlashBackground(self, false);
            }
        }));

    public static void FlashElementBackground(this FrameworkElement element)
    {
        string elementTypeName;

        switch (element)
        {
            case Border border:
                elementTypeName = "Border";
                if (border.Background == null) border.Background = new SolidColorBrush(Colors.Transparent);
                break;
            case Panel panel:
                elementTypeName = "Panel";
                if (panel.Background == null) panel.Background = new SolidColorBrush(Colors.Transparent);
                break;
            case Control control:
                elementTypeName = "Control";
                if (control.Background == null) control.Background = new SolidColorBrush(Colors.Transparent);
                break;
            // More case statements can be added here to cover more scenarios...
            default:
                return;
        }

        var colorAnimation = new ColorAnimationUsingKeyFrames();
        var keyFrame1 = new LinearColorKeyFrame { KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromMilliseconds(0)), Value = Colors.Transparent };
        var keyFrame2 = new LinearColorKeyFrame { KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromMilliseconds(500)), Value = Colors.Red };
        colorAnimation.KeyFrames.Add(keyFrame1);
        colorAnimation.KeyFrames.Add(keyFrame2);

        Storyboard.SetTarget(colorAnimation, element);
        Storyboard.SetTargetProperty(colorAnimation, $"({elementTypeName}.Background).(SolidColorBrush.Color)");

        var storyboard = new Storyboard
        {
            RepeatBehavior = RepeatBehavior.Forever,
            AutoReverse = true
        };
        storyboard.Children.Add(colorAnimation);
        storyboard.Begin();
    }
}
Run Code Online (Sandbox Code Playgroud)

XAML

<Border local:Util.ShowFlashBackground="{Binding IsNetworkConnectivityOk}">
    <TextBlock Text="LAN/WLAN" />
</Border>

<Border local:Util.ShowFlashBackground="{Binding IsSynchronisingOk}"
        Grid.Column="1">
    <TextBlock Text="SYNC" />
</Border>
Run Code Online (Sandbox Code Playgroud)

希望这可以帮助!