Place storyboard in application resources
Asked Answered
F

3

12

I need to use the same storyboard in several places therefore I placed the storyboard inside my Application.Resources . When I try to execute the storyboard the only problem is that I need to reference the target that I want to animate. Here is my storyboard:

    <System:String x:Key="target">border2</System:String>
    <Storyboard x:Key="stHeight">
        <DoubleAnimationUsingKeyFrames 
            Storyboard.TargetProperty="(FrameworkElement.Height)" 
            Storyboard.TargetName="{DynamicResource target}"> 
            <EasingDoubleKeyFrame KeyTime="0:0:1" Value="90">
                <EasingDoubleKeyFrame.EasingFunction>
                    <CircleEase EasingMode="EaseOut"/>
                </EasingDoubleKeyFrame.EasingFunction>
            </EasingDoubleKeyFrame>
        </DoubleAnimationUsingKeyFrames>
    </Storyboard>

the way I animate a different object's height is by changing the dynamic resource target. I was able to do so when the storyboard was in the current window. But now that I want to place it in the application resources I don't know how to reference the target property.


EDIT

The solution that I posted earlier works nice but sometimes it is hard to create complex animations with code. so another alternate solution that I worked out was to create the storyboard with expression blend. so I drag a random control to the main window in expression blend and create a random animation. let's say the animation comes out as:

         <Storyboard x:Key="Storyboard1">
            <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.X)" Storyboard.TargetName="grid">
                <EasingDoubleKeyFrame KeyTime="0:0:1" Value="90"/>
            </DoubleAnimationUsingKeyFrames>
            <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.Y)" Storyboard.TargetName="grid">
                <EasingDoubleKeyFrame KeyTime="0:0:1" Value="103"/>
            </DoubleAnimationUsingKeyFrames>
            <PointAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransformOrigin)" Storyboard.TargetName="grid">
                <EasingPointKeyFrame KeyTime="0:0:1" Value="0.75,0.5"/>
            </PointAnimationUsingKeyFrames>
            <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[2].(RotateTransform.Angle)" Storyboard.TargetName="grid">
                <EasingDoubleKeyFrame KeyTime="0:0:1" Value="75"/>
            </DoubleAnimationUsingKeyFrames>
        </Storyboard>

then I copy that code and paste it in on my working window NOT IN THE APP.XAML.

and then in my code let's say I have a:

<Border Name="brdBorder" BorderBrush="Silver" BorderThickness="1" Margin="328,104,0,0"  Background="#FFE52E2E" HorizontalAlignment="Left" Width="94" Height="100" VerticalAlignment="Top" >
    <Border.RenderTransform>
        <TransformGroup>
            <ScaleTransform/>
            <SkewTransform/>
            <RotateTransform/>
            <TranslateTransform/>
        </TransformGroup>
    </Border.RenderTransform>
</Border>

for some reason the transform group has to be there in order to animate the object. anyways so let's say that I have that boarder in my working window and I want to animate it with the same animation that I created with expression blend. what I will do in code is:

Storyboard sb1 = FindResource("Storyboard1") as Storyboard;

foreach (var child in sb1.Children)
{
    Storyboard.SetTargetName(child, brdBorder.Name);
}

sb1.Begin(this);

and then I am able to animate that border on my working window. The nice part of this is that I am able to apply the same animation to multiple objects (that is the purpose of creating a resource I think) the problem comes when I try to place the storyboard in a resource dictionary or in the app.xaml file. when I do that, c# is able to find the storyboard but the properties of the storyboard are read only therefore I get the error:

Cannot set a property on object 'System.Windows.Media.Animation.DoubleAnimationUsingKeyFrames' because it is in a read-only state.

The reason why I wanted to do this is to apply the same animation to multiple objects. A work around solution was to build the basic animation with code and then the more complex animation such as the easing function etc save it as a resource. Let me show you what I mean.

In my resources file I placed the following resource:

   <EasingDoubleKeyFrame x:Key="pleaseWork">
        <EasingDoubleKeyFrame.EasingFunction >
            <BackEase EasingMode="EaseOut" Amplitude="1"/>
        </EasingDoubleKeyFrame.EasingFunction>
    </EasingDoubleKeyFrame>

In expresion blend you can build a more complex ease function. Then with code behind I will create a basic storyboard:

DoubleAnimation animation = new DoubleAnimation();
            animation.To = 336;   // final value
            //animation.From = 0;
            //animation.BeginTime = TimeSpan.FromSeconds(0); 
            animation.Duration = new Duration(TimeSpan.FromSeconds(5)); // how much time should animation last
            // here comes the magic:
            // note that I can bind to EasingDoubleKeyFrame in my resource file in xaml
            animation.EasingFunction = ((EasingDoubleKeyFrame)FindResource("pleaseWork")).EasingFunction; // apply the easing function 
            Storyboard.SetTarget(animation, groupBox1);  // what object will be animated?
            Storyboard.SetTargetProperty(animation, new PropertyPath(FrameworkElement.HeightProperty)); // what property will be animated

            Storyboard sb = new Storyboard();
            sb.Children.Add(animation);
            sb.Begin();

This enabled me to use the same storyboard on multiple objects.

Flieger answered 27/6, 2011 at 16:36 Comment(1)
I think it is because border2 does not exist in a global scope it only exists in MainWindow. But I have tried to replace border2 for MainWindow.border2 and it still does not workFlieger
F
9

FINALLY I FOUND THE SOLUTION!!!!!

If you recall the error that I got when placing the storyboard in the app.xaml file was:

Cannot set a property on object 'System.Windows.Media.Animation.DoubleAnimationUsingKeyFrames' because it is in a read-only state.

in other words I could not modify the target property of the storyboard I could just red it. so the solution was to change the Storyboard.TargetName="grid" for Storyboard.TargetName="{binding}"


Let me put everything together:

step 1:

// create your custom storyboard with expression blend or xaml: 

// let's say it comes out as:

<Storyboard x:Key="Storyboard1">
            <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.X)" Storyboard.TargetName="grid">
                <EasingDoubleKeyFrame KeyTime="0:0:1" Value="90"/>
            </DoubleAnimationUsingKeyFrames>
            <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.Y)" Storyboard.TargetName="grid">
                <EasingDoubleKeyFrame KeyTime="0:0:1" Value="103"/>
            </DoubleAnimationUsingKeyFrames>
            <PointAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransformOrigin)" Storyboard.TargetName="grid">
                <EasingPointKeyFrame KeyTime="0:0:1" Value="0.75,0.5"/>
            </PointAnimationUsingKeyFrames>
            <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[2].(RotateTransform.Angle)" Storyboard.TargetName="grid">
                <EasingDoubleKeyFrame KeyTime="0:0:1" Value="75"/>
            </DoubleAnimationUsingKeyFrames>
        </Storyboard>

--

step 2:

// copy and paste  your storyboard to your app.xaml file or to a 
// resource dictionary (if you paste it in a resource dictionary do not 
// forget to merge the dictionaries so that your code is able to find the 
// storyboard as a resource)

<Application x:Class="FilesPro2._1.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             StartupUri="MainWindow.xaml">
    <Application.Resources>


        <Storyboard x:Key="Storyboard1">
            <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.X)" Storyboard.TargetName="{Binding}">
                <EasingDoubleK
   ....
   .....
   ...
   etc

--

step 3

// replace the Storyboard.TargetName="grid" for Storyboard.TargetName="{Binding}". // Your storyboard resource should no look like:

<Application.Resources>


        <Storyboard x:Key="Storyboard1">
            <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.X)" Storyboard.TargetName="{Binding}">
                <EasingDoubleKeyFrame KeyTime="0:0:1" Value="90"/>
            </DoubleAnimationUsingKeyFrames>
            <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.Y)" Storyboard.TargetName="{Binding}">
                <EasingDoubleKeyFrame KeyTime="0:0:1" Value="103"/>
            </DoubleAnimationUsingKeyFrames>
            <PointAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransformOrigin)" Storyboard.TargetName="{Binding}">
                <EasingPointKeyFrame KeyTime="0:0:1" Value="0.75,0.5"/>
            </PointAnimationUsingKeyFrames>
            <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[2].(RotateTransform.Angle)" Storyboard.TargetName="{Binding}">
                <EasingDoubleKeyFrame KeyTime="0:0:1" Value="75"/>
            </DoubleAnimationUsingKeyFrames>
        </Storyboard>

--

step 4

//create a method to make it easy to run the same animation on multiple objects:

void runStoryboard(string storyboardName, string objectName)
{
    Storyboard sb = FindResource(storyboardName) as Storyboard;

    foreach (var child in sb.Children)            
        Storyboard.SetTargetName(child, objectName);

    sb.Begin(this); // do not forget the this keyword
}

--

step 5

    // start your animation with the object you wish to animate
            runStoryboard("Storyboard1", brdBorder.Name);

NOTE IMPORTANT:

by aware that when creating your storyboard with expression blend, sometimes expression blend will create a render transform group to the control you are animating. in this example I where animating a boarder. and for that boarder to be animated it needed to have

           <Border.RenderTransform>
                <TransformGroup>
                    <ScaleTransform/>
                    <SkewTransform/>
                    <RotateTransform/>
                    <TranslateTransform/>
                </TransformGroup>
            </Border.RenderTransform>

In other words, if your animation deals with scaletransform skewtransform etc.. then place that render transform group to all the objects that you plan on animating.


finaly I am able to animate:

<Border Name="brdBorder"  BorderBrush="Silver" BorderThickness="1" Margin="338,6,0,0" Background="#FFE52E2E" HorizontalAlignment="Left" Width="94" Height="38" VerticalAlignment="Top">
    <Border.RenderTransform>
        <TransformGroup>
            <ScaleTransform/>
            <SkewTransform/>
            <RotateTransform/>
            <TranslateTransform/>
        </TransformGroup>
    </Border.RenderTransform>
</Border>

   //   AND

<Button Name="mybutton" Content="Test" Height="20" Click="mybutton_Click">
    <Button.RenderTransform>
        <TransformGroup>
            <ScaleTransform/>
            <SkewTransform/>
            <RotateTransform/>
            <TranslateTransform/>
        </TransformGroup>
    </Button.RenderTransform>
</Button>



AS:

runStoryboard("Storyboard1", brdBorder.Name);
runStoryboard("Storyboard1", mybutton.Name);
Flieger answered 11/8, 2011 at 19:13 Comment(6)
You just could have listened to my advice and set x:Shared to false and you would not be in any of this mess.Dissection
But I needed to call the storyboard from code and when I use your code it worked but not on multiple objects being animated at once..Flieger
You need to set x:Shared to false, it works, i tried it, if you still have trouble you just have to get it twice using FindResource, that way you get two storyboards...Dissection
When x:Shared is false you can set the properties in code which before were readonly, throwing an exception...Dissection
yeah you are right sorry. I'll give you the points :) . thanks for the help. an my solution may look long cause I explained everything. The only thing that you need to have is the method that I showed in my solution and paste any storyboard replacing the target property with a binding resource. I think I explained to much. Thanks againFlieger
I couldn't use "this" keyword in storyboard.Begin() and FindResource is not available. Any help?Yahoo
D
26

You do not need to reference the target anywhere, you can just create one locally, this is one of the great features of dynamic resource lookup, e.g.

<!-- No more target here! -->
<Application.Resources>
    <Storyboard x:Key="SB_Height" x:Shared="False">
        <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(FrameworkElement.Height)"
                Storyboard.TargetName="{DynamicResource AnimationTarget}">
            <EasingDoubleKeyFrame KeyTime="0:0:1" Value="90">
                <EasingDoubleKeyFrame.EasingFunction>
                    <CircleEase EasingMode="EaseOut" />
                </EasingDoubleKeyFrame.EasingFunction>
            </EasingDoubleKeyFrame>
        </DoubleAnimationUsingKeyFrames>
    </Storyboard>
</Application.Resources>
<!-- Somewhere else... -->
<Button Name="mybutton" Content="Test" Height="20">
    <Button.Resources>
        <sys:String x:Key="AnimationTarget">mybutton</sys:String>
    </Button.Resources>
    <Button.Triggers>
        <EventTrigger RoutedEvent="Button.Click">
            <BeginStoryboard Storyboard="{StaticResource SB_Height}"/>
        </EventTrigger>
    </Button.Triggers>
</Button>
Dissection answered 11/8, 2011 at 15:2 Comment(5)
if with that button I want to animate something else then I will change the string resource AnimationTarget to something else right? why is it that when I change that to something else it does not work? it works when I animate that button.Flieger
I just created the resource: <System:String x:Key="AnimationTarget">brdBorder</System:String> and it worked.Flieger
thanks a lot for the help. when I want to run the same storyboard on different objects simultaneously it does not work. I need to run the storyboards from code.Flieger
DynamicResource doesn't exist in uwp. Does there exist a similar solution like this one?Hydraulics
@Briefkasten: I do not know. If you want to animate the element that references the storyboard, you should be able to just drop the Storyboard.TargetName entirely. I do not have UWP tools installed and cannot think of anything that would work...Dissection
F
9

FINALLY I FOUND THE SOLUTION!!!!!

If you recall the error that I got when placing the storyboard in the app.xaml file was:

Cannot set a property on object 'System.Windows.Media.Animation.DoubleAnimationUsingKeyFrames' because it is in a read-only state.

in other words I could not modify the target property of the storyboard I could just red it. so the solution was to change the Storyboard.TargetName="grid" for Storyboard.TargetName="{binding}"


Let me put everything together:

step 1:

// create your custom storyboard with expression blend or xaml: 

// let's say it comes out as:

<Storyboard x:Key="Storyboard1">
            <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.X)" Storyboard.TargetName="grid">
                <EasingDoubleKeyFrame KeyTime="0:0:1" Value="90"/>
            </DoubleAnimationUsingKeyFrames>
            <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.Y)" Storyboard.TargetName="grid">
                <EasingDoubleKeyFrame KeyTime="0:0:1" Value="103"/>
            </DoubleAnimationUsingKeyFrames>
            <PointAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransformOrigin)" Storyboard.TargetName="grid">
                <EasingPointKeyFrame KeyTime="0:0:1" Value="0.75,0.5"/>
            </PointAnimationUsingKeyFrames>
            <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[2].(RotateTransform.Angle)" Storyboard.TargetName="grid">
                <EasingDoubleKeyFrame KeyTime="0:0:1" Value="75"/>
            </DoubleAnimationUsingKeyFrames>
        </Storyboard>

--

step 2:

// copy and paste  your storyboard to your app.xaml file or to a 
// resource dictionary (if you paste it in a resource dictionary do not 
// forget to merge the dictionaries so that your code is able to find the 
// storyboard as a resource)

<Application x:Class="FilesPro2._1.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             StartupUri="MainWindow.xaml">
    <Application.Resources>


        <Storyboard x:Key="Storyboard1">
            <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.X)" Storyboard.TargetName="{Binding}">
                <EasingDoubleK
   ....
   .....
   ...
   etc

--

step 3

// replace the Storyboard.TargetName="grid" for Storyboard.TargetName="{Binding}". // Your storyboard resource should no look like:

<Application.Resources>


        <Storyboard x:Key="Storyboard1">
            <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.X)" Storyboard.TargetName="{Binding}">
                <EasingDoubleKeyFrame KeyTime="0:0:1" Value="90"/>
            </DoubleAnimationUsingKeyFrames>
            <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.Y)" Storyboard.TargetName="{Binding}">
                <EasingDoubleKeyFrame KeyTime="0:0:1" Value="103"/>
            </DoubleAnimationUsingKeyFrames>
            <PointAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransformOrigin)" Storyboard.TargetName="{Binding}">
                <EasingPointKeyFrame KeyTime="0:0:1" Value="0.75,0.5"/>
            </PointAnimationUsingKeyFrames>
            <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[2].(RotateTransform.Angle)" Storyboard.TargetName="{Binding}">
                <EasingDoubleKeyFrame KeyTime="0:0:1" Value="75"/>
            </DoubleAnimationUsingKeyFrames>
        </Storyboard>

--

step 4

//create a method to make it easy to run the same animation on multiple objects:

void runStoryboard(string storyboardName, string objectName)
{
    Storyboard sb = FindResource(storyboardName) as Storyboard;

    foreach (var child in sb.Children)            
        Storyboard.SetTargetName(child, objectName);

    sb.Begin(this); // do not forget the this keyword
}

--

step 5

    // start your animation with the object you wish to animate
            runStoryboard("Storyboard1", brdBorder.Name);

NOTE IMPORTANT:

by aware that when creating your storyboard with expression blend, sometimes expression blend will create a render transform group to the control you are animating. in this example I where animating a boarder. and for that boarder to be animated it needed to have

           <Border.RenderTransform>
                <TransformGroup>
                    <ScaleTransform/>
                    <SkewTransform/>
                    <RotateTransform/>
                    <TranslateTransform/>
                </TransformGroup>
            </Border.RenderTransform>

In other words, if your animation deals with scaletransform skewtransform etc.. then place that render transform group to all the objects that you plan on animating.


finaly I am able to animate:

<Border Name="brdBorder"  BorderBrush="Silver" BorderThickness="1" Margin="338,6,0,0" Background="#FFE52E2E" HorizontalAlignment="Left" Width="94" Height="38" VerticalAlignment="Top">
    <Border.RenderTransform>
        <TransformGroup>
            <ScaleTransform/>
            <SkewTransform/>
            <RotateTransform/>
            <TranslateTransform/>
        </TransformGroup>
    </Border.RenderTransform>
</Border>

   //   AND

<Button Name="mybutton" Content="Test" Height="20" Click="mybutton_Click">
    <Button.RenderTransform>
        <TransformGroup>
            <ScaleTransform/>
            <SkewTransform/>
            <RotateTransform/>
            <TranslateTransform/>
        </TransformGroup>
    </Button.RenderTransform>
</Button>



AS:

runStoryboard("Storyboard1", brdBorder.Name);
runStoryboard("Storyboard1", mybutton.Name);
Flieger answered 11/8, 2011 at 19:13 Comment(6)
You just could have listened to my advice and set x:Shared to false and you would not be in any of this mess.Dissection
But I needed to call the storyboard from code and when I use your code it worked but not on multiple objects being animated at once..Flieger
You need to set x:Shared to false, it works, i tried it, if you still have trouble you just have to get it twice using FindResource, that way you get two storyboards...Dissection
When x:Shared is false you can set the properties in code which before were readonly, throwing an exception...Dissection
yeah you are right sorry. I'll give you the points :) . thanks for the help. an my solution may look long cause I explained everything. The only thing that you need to have is the method that I showed in my solution and paste any storyboard replacing the target property with a binding resource. I think I explained to much. Thanks againFlieger
I couldn't use "this" keyword in storyboard.Begin() and FindResource is not available. Any help?Yahoo
P
1

For a

Windows Phone 8.1 Application

I faced the same problem and I add to mix many solutions to accomplish this.

This is my App.xaml file

<Application.Resources>
        <ResourceDictionary>
            <Storyboard x:Key="ShowMenuAnimationKey">
                <DoubleAnimation
                            Storyboard.TargetName="{Binding}" //this is important
                            Storyboard.TargetProperty="(Canvas.Left)"
                            From="0" To="400" Duration="0:0:1">
                </DoubleAnimation>
            </Storyboard>
        </ResourceDictionary>
    </Application.Resources>

In my page, I have something like this (page.xaml)

<Grid x:Name="ViewPanel" 
                        Canvas.Left="0"
                        Canvas.ZIndex="1">
//Etc ....
</Grid>

Now, you bind this method to an event (button tap or something else)

page.xaml.cs

private void MenuPanel_OnTap(object sender, GestureEventArgs e)
        {
            Storyboard sb = App.Current.Resources["ShowMenuAnimationKey"] as Storyboard;
            sb.Stop();
            Storyboard.SetTarget(sb, ViewPanel);
            sb.Begin();
        }

I hope that will help!

Pyrophoric answered 17/12, 2015 at 9:38 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.