Sinatr
Sinatr

Reputation: 21969

Target window storyboard from button event (sounds too simple aye)

I have complicated animation, which has to run at startup and every time when mouse leaves buttons (I have many buttons)

<Window.Triggers>
    <EventTrigger RoutedEvent="Loaded">
        <BeginStoryboard x:Name="storyboard">
            <Storyboard>
                ...
            </Storyboard>
        </BeginStoryboard>
    </EventTrigger>

    <!-- I have to do this for every button -->
    <EventTrigger RoutedEvent="MouseEnter" SourceName="button1">
        <SeekStoryboard BeginStoryboardName="storyboard" Offset="0"/>
        <PauseStoryboard BeginStoryboardName="storyboard"/>
    </EventTrigger>
    <EventTrigger RoutedEvent="MouseLeave" SourceName="button1">
        <ResumeStoryboard BeginStoryboardName="storyboard"/>
    </EventTrigger>
</Window.Triggers>

Instead of creating new animations I reuse just one, which was played in Loaded, this is why here Seek.., Pause.. and ResumeStoryboard.

Everything works fine, but there is a lot of duplicated code.

I started thinking of making a style for buttons to avoid WET xaml coding. At first, I tried this (simply to see if idea is good)

<Button x:Name="button1">
    <Button.Triggers>
        <EventTrigger RoutedEvent="MouseEnter">
            <SeekStoryboard BeginStoryboardName="storyboard" Offset="0"/>
            ...

Not very surprisingly I got an exception

System.Windows.Media.Animation Warning: 6 : Unable to perform action because the specified Storyboard was never applied to this object for interactive control.; Action='Seek'; Storyboard='System.Windows.Media.Animation.Storyboard'; Storyboard.HashCode='56868664'; Storyboard.Type='System.Windows.Media.Animation.Storyboard'; TargetElement='System.Windows.Controls.Button'; TargetElement.HashCode='23765798'; TargetElement.Type='System.Windows.Controls.Button'

I have feeling it should be something to do with Storyboard.Target..., but trying to give window name produces same error

<Window x:Name="window"
    ...
    <SeekStoryboard BeginStoryboardName="storyboard" Offset="0" Storyboard.TargetName="window"/>

What should I do? My aim is to have button style (defined in window resources), but I have problem to target storyboard within window triggers from child control event trigger.

Upvotes: 1

Views: 556

Answers (1)

King King
King King

Reputation: 63317

I understand that you have many buttons. So the Storyboard in Window.Triggers should be used for all the buttons (animate once when the window is loaded). However the Storyboard requires a specific target, how could you set it for all the buttons? We can only duplicate the code and target each one by its name. That's the first problem.

The second problem appears while attempting to solve the first problem. When placing all the SeekStoryboard, PauseStoryboard, ... inside a Button's Style, it cannot find the Storyboard's name in the Style scope. That means the only way to fix this is place the Storyboard right inside the Button's Style. But then we cannot access this Storyboard easily inside Window.Triggers. However to solve the first problem we should also not place the Storyboard inside Window.Triggers scope. Why don't we trigger the Storyboard when the Button's Loaded instead? Then we can place all inside a Button's Style. The following code should solve all your problems:

<Window.Resources>
    <Storyboard x:Key="sb">
      <!-- more code here ... -->
    </Storyboard>       
    <Style TargetType="Button">
        <Style.Triggers>
           <DataTrigger Binding="{Binding Visibility,  
                             RelativeSource={RelativeSource AncestorType=Window},
                             Mode=OneTime}" Value="Visible">
               <DataTrigger.EnterActions>
                 <StaticResource ResourceKey="beginSb"/>
               </DataTrigger.EnterActions>
           </DataTrigger>
           <EventTrigger RoutedEvent="MouseEnter">
               <SeekStoryboard BeginStoryboardName="storyboard" Offset="0"/>
               <PauseStoryboard BeginStoryboardName="storyboard"/>
           </EventTrigger>
           <EventTrigger RoutedEvent="MouseLeave">
               <ResumeStoryboard BeginStoryboardName="storyboard"/>
           </EventTrigger>
        </Style.Triggers>
    </Style>
</Window.Resources>

Upvotes: 1

Related Questions