【问题标题】:UWP: Sharing a storyboardUWP:共享故事板
【发布时间】:2018-03-01 19:43:54
【问题描述】:

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

但是,我找不到在 UWP 中执行相同操作的方法。在 WPF 中使用 DataTrigger 和 BeginStoryboard 似乎会自动关联 Storyboard.TargetName 但在 UWP 中我必须使用 Interactions 包并尝试将情节提要与控件以与情节提要未与控件关联的错误相同的方式关联。 ..

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

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

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

谢谢。

【问题讨论】:

    标签: xaml uwp windows-10-universal uwp-xaml


    【解决方案1】:

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

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


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

    这个想法是创建一个名为ShowFlashBackgroundbool 属性,该属性可以附加到具有Background 属性的元素,例如BorderGrid 或任何Control

    然后,只要将此属性设置为true(例如由IsNetworkConnectivityOk 触发),就调用扩展方法FlashElementBackground 来启动彩色动画。

    守则

    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();
        }
    }
    

    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>
    

    希望这会有所帮助!

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-08-16
      • 1970-01-01
      • 2018-01-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-11-16
      相关资源
      最近更新 更多