关于xaml:UWP:共享一个故事板

UWP: Sharing a storyboard

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
    <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程序包,然后尝试以与报文面板不与控件相关联的错误相同的方式将故事板与控件关联。 ..

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
<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中找到做到这一点的唯一方法是拥有多个相同的情节提要板,每个情节提要板都引用了将影响的控件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
<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>

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

谢谢。


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

但是如果要同时为两个元素设置动画,该怎么办?然后这种方法将行不通。这就是为什么要为要设置动画的每个元素创建一个新的Storyboard的原因。

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

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

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

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
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

1
2
3
4
5
6
7
8
<Border local:Util.ShowFlashBackground="{Binding IsNetworkConnectivityOk}">
    <TextBlock Text="LAN/WLAN" />
</Border>

<Border local:Util.ShowFlashBackground="{Binding IsSynchronisingOk}"
        Grid.Column="1">
    <TextBlock Text="SYNC" />
</Border>

希望这会有所帮助!