0
votes

With the following definitions, OnSignalChanged never gets executed when Signal in the INotifyPropertyChanged-compliant view model changes its value and, of course, the animation doesn't play. Am I misunderstanding something fundamental? TestCommand simply sets Signal from false to true.

Public Class StoryboardSignalManager
    Public Shared SignalProperty As DependencyProperty = DependencyProperty.RegisterAttached("Signal",
                                                                                         GetType(Boolean),
                                                                                         GetType(StoryboardSignalManager),
                                                                                         New PropertyMetadata(AddressOf OnSignalChanged))
    Public Shared Function GetSignal(d As DependencyObject) As Boolean?
        Return DirectCast(d.GetValue(SignalProperty), Boolean?)
    End Function
    Public Shared Sub SetSignal(d As DependencyObject, value As Object)
        d.SetValue(SignalProperty, value)
    End Sub

    Private Shared Sub OnSignalChanged(d As DependencyObject, e As DependencyPropertyChangedEventArgs)
        Dim value As Boolean = DirectCast(e.NewValue, Boolean)
        Dim storyboard As Storyboard = DirectCast(d, Storyboard)

        If value Then
            storyboard.Begin()
        Else
            storyboard.Stop()
        End If
    End Sub
End Class
<Grid>
    <Grid.Resources>
        <Storyboard x:Key="Fade" local:StoryboardSignalManager.Signal="{Binding Signal}">
            <DoubleAnimationUsingKeyFrames Storyboard.Target="{Binding ElementName=TextBlock}" Storyboard.TargetProperty="Opacity">
                <EasingDoubleKeyFrame KeyTime="00:00:00.00" Value="1" />
                <EasingDoubleKeyFrame KeyTime="00:00:01.00" Value="0" />
                <EasingDoubleKeyFrame KeyTime="00:00:02.00" Value="1" />
            </DoubleAnimationUsingKeyFrames>
        </Storyboard>
    </Grid.Resources>
    <StackPanel>
        <TextBlock x:Name="TextBlock" Text="test" />
        <Button Width="100" Height="50" Command="{Binding TestCommand}" />
    </StackPanel>
</Grid>
1
Can you post the code that sets the Signal property?tgpdyk

1 Answers

1
votes

I tested your code out (albeit in C#), and empirically it seems like your PropertyChangedCallback is not being called because the storyboard is a resource, and it doesn't seem to be loaded at the right time for the attached property to be attached so to say...

To see what I'm talking about, you can add a Loaded event handler for your usercontrol/view code behind

    private void UserControl_Loaded(object sender, RoutedEventArgs e)
    {
        var board = (this.FindResource("Fade") as Storyboard);
    }

If I'm not mistaken, this will answer your question about the callback not being executed; upon control loading, you will force a resource load with FindResource, which will make the attached property work properly.

However, making this work seems to cause a stackoverflow exception located around storyboard.Begin() in the callback function. I do not know why this happens (perhaps some hidden WPF magic... someone let me know if you know why please!)

Given this, I'd suggest working your animation into something like this instead:

<Grid local:StoryBoardSignalManager.Signal="{Binding Signal, Mode=TwoWay}" >
    <Grid.Resources>
        <Style x:Key="FadeAnimationStyle" TargetType="TextBlock">
            <Style.Triggers>
                <DataTrigger Binding="{Binding Path=(local:StoryBoardSignalManager.Signal), RelativeSource={RelativeSource AncestorType=Grid}}" Value="True">
                    <DataTrigger.EnterActions>
                        <BeginStoryboard>
                            <Storyboard>
                                <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="Opacity">
                                    <EasingDoubleKeyFrame KeyTime="00:00:00.00" Value="1" />
                                    <EasingDoubleKeyFrame KeyTime="00:00:01.00" Value="0" />
                                    <EasingDoubleKeyFrame KeyTime="00:00:02.00" Value="1" />
                                </DoubleAnimationUsingKeyFrames>
                            </Storyboard>
                        </BeginStoryboard>
                    </DataTrigger.EnterActions>
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </Grid.Resources>
    <StackPanel>
        <TextBlock x:Name="TextBlock" Text="test" Style="{StaticResource FadeAnimationStyle}"/>
        <Button Width="100" Height="50" Command="{Binding TestCommand}" />
    </StackPanel>
</Grid>