WPF - animate image source change - wpf

I'm pretty new to WPF but I think what I need to do is relatively simple. I need to create an image "animation", where I am changing an image source every .25 of a second.
I have a folder named "animation" with images 1 to 25 png live (named 1.png, 2.png... 25.png). Each image correlate to different frame of my animation.
I want to write xaml to change the image from 1 to 2, from 2 to 3, for 3 to 4 etc every .25 seconds until it gets to the 25th image, then it should loop back to the start.
I most likely will need to write some c# to do this. I want it to run on a thread that can interact with the UI (as in update the image) but not block the UI thread.
Thanks in advance!

A pure XAML solution could look like this, of course with different images and timings.
<Image>
<Image.Triggers>
<EventTrigger RoutedEvent="Loaded">
<BeginStoryboard>
<Storyboard RepeatBehavior="Forever">
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Source"
Duration="0:0:2">
<DiscreteObjectKeyFrame KeyTime="0:0:0">
<DiscreteObjectKeyFrame.Value>
<BitmapImage UriSource="C:\Users\Public\Pictures\Sample Pictures\Koala.jpg"/>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
<DiscreteObjectKeyFrame KeyTime="0:0:1">
<DiscreteObjectKeyFrame.Value>
<BitmapImage UriSource="C:\Users\Public\Pictures\Sample Pictures\Penguins.jpg"/>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Image.Triggers>
</Image>

Related

How to reuse interactions and interactivity attached to a grid

I am trying to make use of interactivity and interactions libraries in wpf app. I need it to work on a grid, and it works well, but now I need this stuff in multiple grids and I cant find a way of reusing it. Here is the xaml
<Grid>
<i:Interaction.Behaviors>
<ei:DataStateBehavior Binding="{Binding KeepAlive}"
FalseState="InactiveState"
TrueState="ActiveState"
Value="false" />
</i:Interaction.Behaviors>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup>
<VisualState x:Name="ActiveState" />
<VisualState x:Name="InactiveState">
<Storyboard>
<ObjectAnimationUsingKeyFrames
Storyboard.TargetName="ActiveContainer"
Storyboard.TargetProperty="(Control.IsEnabled)">
<DiscreteObjectKeyFrame KeyTime="0:0:0">
<DiscreteObjectKeyFrame.Value>
<system:Boolean>False</system:Boolean>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames
Storyboard.TargetName="InactiveContainer"
Storyboard.TargetProperty="(UIElement.Visibility)">
<DiscreteObjectKeyFrame KeyTime="0">
<DiscreteObjectKeyFrame.Value>
<Visibility>Visible</Visibility>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Border>
<Grid>
Content comes here, Texboxes, labels when active or inactive etc.
</Grid>
</Border>
</Grid>
It works great, but how can I refactor the above code so I can reuse the exact same behaviour on multiple grids?
Thank you
A few approach immediately come up: style or resource. But Blend behavior cannot work when declared in resources. You can use attached property. See:
How to add a Blend Behavior in a Style Setter
I created generic behaviour, from which you could simply inherit and add to your style
see my answer below
https://stackoverflow.com/a/31292989/4711853

Fade out animation keeps repeating, possibly triggers itself?

I'm trying to create a very simple fade out effect that should happen whenever the user control's visibility changes to hidden.
To do this I created a storyboard that is triggered whenever the visibility of the user control changes to Hidden. It then first changes the visibility to visible again so I can animate it. It then changes the opacity over time, and finally changes the visibility to hidden. However this causes the animation to repeat forever.
When changing the control from visible to collapsed in the final frame it does work once, but then the user control will never become visible again (I listen for the IsVisibleChanged event and it never gets triggered after it has collapsed once, even though I literally have the code control.Visibility = Visibility.Visible; at the end of that event handler.
So how do I make sure the animation is only triggered once, preferably without code behind so I can just add this fadeout control in a nice resource dictionary and it will always work. I've seen a few questions related to this on SO especially this one but I cant get it to work.
The code I have so far:
<UserControl.Style>
<Style TargetType="{x:Type UserControl}">
<Style.Triggers>
<Trigger Property="Visibility" Value="Hidden">
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0">
<DiscreteObjectKeyFrame.Value>
<Visibility>Visible</Visibility>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
<DoubleAnimation Storyboard.TargetProperty="Opacity" From="1.0" To="0.0" Duration="0:0:3" FillBehavior="Stop"/>
<ObjectAnimationUsingKeyFrames BeginTime="0:0:3.5" Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0">
<DiscreteObjectKeyFrame.Value>
<Visibility>Hidden</Visibility>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
</Trigger>
</Style.Triggers>
</Style>
</UserControl.Style>
Would it work fine if you just removed the ObjectAnimation that sets the Visibility to Hidden? So the control is technically visible, but with opacity 0?

Animating height in WIndows Phone

I have a grid containing two elements, a search form and a custom UC to show items. When my page loads, the viewer control is hidden. I ant to animate it in with a storyboard when a button on the search form is clicked. I'm stuck at the To=... part of the animation. I want to animate it to the height of the parent container and have it slid up from the bottom. Is this possible?
For reference, this is my tree structure:
Page resources:
<Storyboard x:Name="animateViewSlide" Storyboard.TargetName="SearchForm" >
<DoubleAnimation Storyboard.TargetProperty="Height" To="???" Duration="100"/>
</Storyboard>
Control body:
<Grid x:Name="LayoutRoot">
<Grid x:Name="SearchForm" Margin="6,6,6,70">
<uc:AwesomeViewer x:Name="awesomeView" Awesomeness="{Binding SelectedAwesomeThing}" Visibility="Collapsed"/>
</Grid>
UPDATE
I did figure out a bit better way of doing this, instead of using the Margin to step through the animation, just animate the Y axis with a TranslateTransform like;
<Grid x:Name="ItemToMove"
Visibility="{Binding Condition, Converter={StaticResource boolVisConv}}" VerticalAlignment="Bottom" HorizontalAlignment="Center" Margin="30,0,0,-32" Opacity="0">
<Grid.RenderTransform>
<TranslateTransform x:Name="notified" Y="40" />
</Grid.RenderTransform>
<Grid.Triggers>
<EventTrigger RoutedEvent="Grid.Loaded">
<BeginStoryboard>
<Storyboard>
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="notified" Storyboard.TargetProperty="Y">
<SplineDoubleKeyFrame KeyTime="0:0:1.25" Value="0" />
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="ItemToMove" Storyboard.TargetProperty="Opacity">
<SplineDoubleKeyFrame KeyTime="0:0:1.55" Value="1" />
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Grid.Triggers>
END UPDATE
Your trouble is going to stem from trying to animate an automatically calculated value (Height) so I would scrap that idea unless you want to use hard coded size values on your Parents/Children or just assume for example that if the app is restricted to Portrait Mode only....then your "To" value is 800px, except I imagine that's not what you're looking for.
Now then, you would usually hope you could just feed the rendered size into your "To" value, like maybe via To="{Binding ActualHeight, ElementName=LayoutRootOROTHERParentContainer}" right? Yet nay, that won't work either...
So while someone else may have a better way, the only way I've found is to instead leave the Auto calculated sizes alone to do their thing, and instead animate a stepping of the object Margin via ObjectAnimationUsingKeyFrames which when paired with an Opacity transition via DoubleAnimationUsingKeyFrames actually looks quite nice, except it will add a bunch of additional XAML for the process. So as a concept you might try something more like this (however if you come across a better solution, I'd sure like to know it also. As a designer for a living, I run into this relatively often.)
Concept Example;
(The Storyboard)
<Storyboard x:Name="Kaboom">
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(FrameworkElement.Margin)" Storyboard.TargetName="border">
<DiscreteObjectKeyFrame KeyTime="0:0:0.1">
<DiscreteObjectKeyFrame.Value>
<Thickness>0,750,0,0</Thickness>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
<DiscreteObjectKeyFrame KeyTime="0:0:0.2">
<DiscreteObjectKeyFrame.Value>
<Thickness>0,600,0,0</Thickness>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
<DiscreteObjectKeyFrame KeyTime="0:0:0.3">
<DiscreteObjectKeyFrame.Value>
<Thickness>0,550,0,0</Thickness>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
<DiscreteObjectKeyFrame KeyTime="0:0:0.4">
<DiscreteObjectKeyFrame.Value>
<Thickness>0,500,0,0</Thickness>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
<DiscreteObjectKeyFrame KeyTime="0:0:0.5">
<DiscreteObjectKeyFrame.Value>
<Thickness>0,450,0,0</Thickness>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
<DiscreteObjectKeyFrame KeyTime="0:0:0.6">
<DiscreteObjectKeyFrame.Value>
<Thickness>0,400,0,0</Thickness>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
<DiscreteObjectKeyFrame KeyTime="0:0:0.7">
<DiscreteObjectKeyFrame.Value>
<Thickness>0,350,0,0</Thickness>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
<DiscreteObjectKeyFrame KeyTime="0:0:0.8">
<DiscreteObjectKeyFrame.Value>
<Thickness>0,300,0,0</Thickness>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
<DiscreteObjectKeyFrame KeyTime="0:0:0.9">
<DiscreteObjectKeyFrame.Value>
<Thickness>0,250,0,0</Thickness>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
<DiscreteObjectKeyFrame KeyTime="0:0:1">
<DiscreteObjectKeyFrame.Value>
<Thickness>0,200,0,0</Thickness>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
<DiscreteObjectKeyFrame KeyTime="0:0:1.1">
<DiscreteObjectKeyFrame.Value>
<Thickness>0,150,0,0</Thickness>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
<DiscreteObjectKeyFrame KeyTime="0:0:1.2">
<DiscreteObjectKeyFrame.Value>
<Thickness>0,100,0,0</Thickness>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
<DiscreteObjectKeyFrame KeyTime="0:0:1.3">
<DiscreteObjectKeyFrame.Value>
<Thickness>0,50,0,0</Thickness>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
<DiscreteObjectKeyFrame KeyTime="0:0:1.4">
<DiscreteObjectKeyFrame.Value>
<Thickness>0</Thickness>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)" Storyboard.TargetName="border">
<EasingDoubleKeyFrame KeyTime="0:0:0.6" Value="0.01"/>
<EasingDoubleKeyFrame KeyTime="0:0:1.6" Value="1"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
A quick adhoc example of the concept in action;
<Grid Height="800" Width="480" x:Name="ParentContainer">
<!-- Your SearchForm thingy with its funky margins already set like your example -->
<Rectangle Margin="6,6,6,70" Fill="Green" />
<Button Height="100" Width="100" Content="Click Me!" HorizontalAlignment="Left" VerticalAlignment="Top">
<!-- Quick button to show it in action -->
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<ei:ControlStoryboardAction Storyboard="{StaticResource Kaboom}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
<!-- Your Awesomeness -->
<Border x:Name="border" Width="300" Background="Blue" HorizontalAlignment="Right" Margin="0,800,0,0" Opacity="0">
<TextBlock x:Name="textBlock" Text="OH
SNAP!!!
Say
WHAAA??"
TextAlignment="Center" VerticalAlignment="Center"
FontSize="40" FontWeight="Bold" Foreground="White"/>
</Border>
</Grid>
Anyway, probably not exactly what you're hoping for, but is an effective alternative especially with a little tweaking unless like I said, you want to hardcode your object sizes so you have something viable to set your "To" value to...which, in the case of a phone that's restricted to the standard 800x480 Portrait Mode Only you may very well want to. However if you don't have that option, I've pulled off some pretty nifty designs using this method instead.
Hope this helps and best of luck!

Trouble animating an image, Seems Storyboard doesn't start

My Code is posted Below. I am creating an image, and attempting to add animation to it. I am sure it was working at one point, I made a small change, it failed I undid the change (literally undo) and now it continues to fail. I am attempting to make an image change ever half second to another image, then change back after another half second.
<Image Height="30" HorizontalAlignment="Left" Margin="198,90,0,0" Name="OldMan" Stretch="Fill" VerticalAlignment="Top" Width="30" Source="/TestingWpF;component/Images/Old1.png" >
<Image.Triggers>
<EventTrigger RoutedEvent="Window.Loaded">
<BeginStoryboard>
<Storyboard >
<ObjectAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="OldMan" Storyboard.TargetProperty="(Image.Source)" RepeatBehavior="Forever">
<DiscreteObjectKeyFrame KeyTime="00:00:00.5000000">
<DiscreteObjectKeyFrame.Value>
<BitmapImage UriSource="/TestingWpF;component/Images/Old1.png" />
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
<DiscreteObjectKeyFrame KeyTime="00:00:01.0000000">
<DiscreteObjectKeyFrame.Value>
<BitmapImage UriSource="/TestingWpF;component/Images/Old2.png" />
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Image.Triggers>
</Image>
The problem is that you don't have your duration set. You'll just get the first keyframe, but not the next. Try adding something like Duration="00:00:01.5" to ObjectAnimationUsingKeyFrames

changing tooltip in visualstatemanager

i want to be able to change the tooltip on Checked and Unchecked, i tried:
<VisualState x:Name="Checked">
<Storyboard>
<ObjectAnimationUsingKeyFrames Duration="0" Storyboard.TargetProperty="ToolTipService.ToolTip" Storyboard.TargetName="btn">
<DiscreteObjectKeyFrame KeyTime="0">
<DiscreteObjectKeyFrame.Value>
<System:String>Button is checked</System:String>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
but it doesnt seem to work, what am i doing wrong?
Once before I also, ran into the same problem. So, I left the VSM away. I made the ToolTip as Resource and applied it for the control. Using the internal state change properties I updated the tooltip value using a converter.
HTH.

Resources