Animate control based on other control - c#

I am creating a media player application and I am trying to animate hiding the controls when the mouse is not inside the program window.
I have a animation setup and working, but I can't think of how to set the EventTrigger to the parent grid rather than the grid I actually want to animate. Essentially I want to set the EventTrigger to be grdMain and animate the height of grdControls.
Animation:
<Window.Resources>
<Style x:Key="FadeInOut" TargetType="Grid">
<Style.Triggers>
<EventTrigger RoutedEvent="Control.MouseEnter">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Duration="0:0:1" To="40" Storyboard.TargetProperty="Height"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="Control.MouseLeave">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Duration="0:0:1" To="0" Storyboard.TargetProperty="Height"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Style.Triggers>
</Style>
</Window.Resources>
Grids:
<Grid x:Name="grdMain" Background="Black">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<MediaElement x:Name="meVideo" IsMuted="True" Stretch="Uniform" MediaOpened="meVideo_MediaOpened" MediaEnded="meVideo_MediaEnded" Grid.RowSpan="2" />
<Grid Grid.Row="1" >
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<ProgressBar x:Name="pgbVideoTimeline" HorizontalAlignment="Stretch" Height="7" Background="#252525" Foreground="Maroon" BorderThickness="0" Grid.Row="1" MouseDown="pgbVideoTimeline_MouseDown" MouseMove="pgbVideoTimeline_MouseMove" MouseUp="pgbVideoTimeline_MouseUp" />
<Grid x:Name="grdControls" Grid.Row="2" Background="#0C0D0D" Height="0" Style="{StaticResource FadeInOut}" >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<StackPanel x:Name="stpPlaybackControls" Orientation="Horizontal" HorizontalAlignment="Left" Grid.Column="0">
<Image x:Name="btnPlayPause" Height="30" Margin="10,0,5,0" ToolTip="Play" Source="Resources/Images/UI/Play.png" MouseEnter="btnVideoControl_MouseEnter" MouseLeave="btnVideoControl_MouseLeave" MouseUp="btnPlayPause_MouseUp" />
<Image x:Name="btnReplay" Height="20" Margin="5,0,5,0" ToolTip="Replay" Source="Resources/Images/UI/Replay.png" MouseEnter="btnVideoControl_MouseEnter" MouseLeave="btnVideoControl_MouseLeave" MouseUp="btnReplay_MouseUp" />
</StackPanel>
<StackPanel x:Name="stpMiscControls" Orientation="Horizontal" HorizontalAlignment="Right" Grid.Column="1">
<Image x:Name="btnFullScreen" Height="25" Margin="5" ToolTip="Fullscreen" Source="Resources/Images/UI/FullScreen.png" MouseEnter="btnVideoControl_MouseEnter" MouseLeave="btnVideoControl_MouseLeave" MouseUp="btnFullScreen_MouseUp" />
<Image x:Name="btnSettings" Height="30" Margin="5,5,10,5" ToolTip="Settings" Source="Resources/Images/UI/Settings.png" MouseEnter="btnVideoControl_MouseEnter" MouseLeave="btnVideoControl_MouseLeave" />
</StackPanel>
</Grid>
</Grid>
</Grid>

The rule when using Trigger (in case TargetName can't be used) is the Trigger should belong to the element which has the properties you want to modify. In this case the Trigger should belong to the grdControls. However you can use DataTrigger to walk up the tree and listen to some other property change of any visual upwards in the tree. The following code should work:
<Style x:Key="FadeInOut" TargetType="Grid">
<Style.Triggers>
<DataTrigger Binding="{Binding IsMouseOver, ElementName=grdMain}"
Value="true">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Duration="0:0:1" To="40"
Storyboard.TargetProperty="Height"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Duration="0:0:1" To="0"
Storyboard.TargetProperty="Height"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.ExitActions>
</DataTrigger>
</Style.Triggers>
</Style>
The second approach close to what Peter Dunno suggested in his comment requires you to have to set the EventTrigger directly in the Triggers property of the main Grid. You can either set the Storyboards directly or define them as Resource. Here is the code for that approach:
<Grid x:Name="grdMain" Background="Black">
<Grid.Triggers>
<EventTrigger RoutedEvent="Control.MouseEnter">
<BeginStoryboard>
<Storyboard TargetName="grdControls">
<DoubleAnimation Duration="0:0:1" To="40"
Storyboard.TargetProperty="Height"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="Control.MouseLeave">
<BeginStoryboard>
<Storyboard TargetName="grdControls">
<DoubleAnimation Duration="0:0:1" To="0"
Storyboard.TargetProperty="Height"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Grid.Triggers>
<!-- remaining code -->
</Grid>
Note that the Style you define now is no longer needed.

Related

WPF Animation : How do i make it slide in?

So I just got into animations and I wanted to do a "Slide out" animation and I managed to do so just fine.
But now I want it to slide in with the click on the same button.
So like I click it and it slides out and then I want it to slide back in when I click it again.
WITHOUT any code behind, so just through xaml
Here is the XAML
<Grid>
<Grid.Resources>
<Storyboard x:Key="TransformImage">
<DoubleAnimation
Storyboard.TargetName="MovingImage"
Storyboard.TargetProperty="(Image.RenderTransform).(TranslateTransform.X)"
By="130" Duration="0:0:0.3">
</DoubleAnimation>
</Storyboard>
<Storyboard x:Key="TransformButton">
<DoubleAnimation
Storyboard.TargetName="btnChange"
Storyboard.TargetProperty="(Button.RenderTransform).(TranslateTransform.X)"
By="130" Duration="0:0:0.3">
</DoubleAnimation>
</Storyboard>
</Grid.Resources>
<Grid.Triggers>
<EventTrigger RoutedEvent="Button.Click" SourceName="btnChange">
<BeginStoryboard Storyboard="{StaticResource TransformImage}"/>
<BeginStoryboard Storyboard="{StaticResource TransformButton}"/>
</EventTrigger>
</Grid.Triggers>
<StackPanel Orientation="Horizontal" Margin="0">
<Image x:Name="MovingImage" Source="logo.png"
MaxWidth="120">
<Image.RenderTransform>
<TranslateTransform />
</Image.RenderTransform>
</Image>
</StackPanel>
<StackPanel
Panel.ZIndex="1"
Height="450"
Width="120"
HorizontalAlignment="Left"
Background="Black"></StackPanel>
<Button Margin="130,0,0,0" Height="40" Width="120"
Content="Show Image" x:Name="btnChange"
HorizontalAlignment="Left" >
<Button.RenderTransform>
<TranslateTransform />
</Button.RenderTransform>
</Button>
</Grid>
You need to store current state of slide animation when clicking on button and based on it run appropriate story board.
For example, you could use ToggleButton that has IsChecked property (or property in view-model).
To avoid duplicate triggers/styles I placed Image and ToggleButton in the same StackPanel.
<Grid>
<Grid.Resources>
<system:Double x:Key="SlideOffSet">130</system:Double>
<Storyboard x:Key="SlideRight">
<DoubleAnimation Storyboard.TargetProperty="(UIElement.RenderTransform).(TranslateTransform.X)"
From="0" To="{StaticResource SlideOffSet}"
Duration="0:0:0.3" />
</Storyboard>
<Storyboard x:Key="SlideLeft">
<DoubleAnimation Storyboard.TargetProperty="(UIElement.RenderTransform).(TranslateTransform.X)"
From="{StaticResource SlideOffSet}" To="0"
Duration="0:0:0.3" />
</Storyboard>
</Grid.Resources>
<StackPanel Orientation="Horizontal" Margin="0">
<StackPanel.Style>
<Style TargetType="StackPanel">
<Style.Triggers>
<DataTrigger Binding="{Binding IsChecked, ElementName=SlideState}" Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard Storyboard="{StaticResource SlideRight}" />
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<BeginStoryboard Storyboard="{StaticResource SlideLeft}" />
</DataTrigger.ExitActions>
</DataTrigger>
</Style.Triggers>
</Style>
</StackPanel.Style>
<StackPanel.RenderTransform>
<TranslateTransform />
</StackPanel.RenderTransform>
<Image Source="logo.png" MaxWidth="120" />
<ToggleButton x:Name="SlideState" Margin="10,0,0,0" Height="40" Width="120" Content="Show Image" />
</StackPanel>
<StackPanel
Panel.ZIndex="1"
Height="450"
Width="120"
HorizontalAlignment="Left"
Background="Black"></StackPanel>
</Grid>
Also, I would not recommend using By property of the DoubleAnimation:
If animation is still running and you click button again, then second animation will be started at wrong position (it will use X value from first animation, which is not finished) - Click button quickly many times and you will see the problem.
Use From and To properties instead.

Disable Storyboard or Animation in WPF

I created a simple notification window with animation and some message. But, can I disable animation or storyboard on eg. MouseEnter event, like a Facebook notification. Gradually reduced the opacity, and when I drag the Mouse on the window, then set opacity to 100%. How to do?
Here is a xaml code:
WindowStyle="None" AllowsTransparency="True" Background="Transparent" >
<Grid x:Name="gridData" RenderTransformOrigin="0,1" MouseRightButtonDown="Window_MouseRightButtonDown" MouseEnter="Grid_MouseEnter">
<Border BorderThickness="1" Background="SkyBlue" BorderBrush="Black" CornerRadius="10">
<StackPanel Margin="20">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="32"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="40"/>
</Grid.ColumnDefinitions>
<TextBlock x:Name="txtTitle" Grid.Row="0" Grid.Column="0" Text="" FontWeight="Bold" VerticalAlignment="Center"/>
<Image x:Name="image" Grid.Row="0" Grid.Column="1" HorizontalAlignment="Left" Visibility="Collapsed"/>
<TextBlock x:Name="txtMessage" Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2" Text="" TextWrapping="Wrap"/>
</Grid>
</StackPanel>
</Border>
<Grid.Triggers>
<EventTrigger RoutedEvent="FrameworkElement.Loaded" >
<BeginStoryboard >
<Storyboard>
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(ScaleTransform.ScaleY)">
<SplineDoubleKeyFrame KeyTime="0:0:0" Value="0"/>
<SplineDoubleKeyFrame KeyTime="0:0:0.5" Value="1"/>
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)">
<SplineDoubleKeyFrame KeyTime="0:0:2" Value="1"/>
<SplineDoubleKeyFrame KeyTime="0:0:4" Value="0"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Grid.Triggers>
<Grid.RenderTransform>
<ScaleTransform ScaleY="1" />
</Grid.RenderTransform>
</Grid>
Add a name to your BeginStoryboard:
<BeginStoryboard Name="ScaleAndFadeOut">
then add another event trigger for a different event, and use a StopStoryboard element:
<Grid.Triggers>
<EventTrigger RoutedEvent="FrameworkElement.Loaded">
...
</EventTrigger>
<EventTrigger RoutedEvent="FrameworkElement.MouseMove">
<StopStoryboard BeginStoryboardName="ScaleAndFadeOut" />
</EventTrigger>
</Grid.Triggers>
MSDN: "How to: Use Event Triggers to Control a Storyboard After It Starts"

WPF Storyboard with TranslateTransform

I have a Storyboard such as
<Storyboard x:Key="NewsFlow">
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.Y)"
Storyboard.TargetName="TextBlock"
RepeatBehavior="Forever" AutoReverse="True">
<EasingDoubleKeyFrame KeyTime="0:0:0" Value="-80"/>
<EasingDoubleKeyFrame KeyTime="0:0:2" Value="80"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
And Grid
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="1*" />
<RowDefinition Height="100" />
<RowDefinition Height="1*" />
</Grid.RowDefinitions>
<Border Grid.Row="1" Background="BlanchedAlmond"></Border>
<TextBlock Grid.Row="1" Text="News" FontSize="40" HorizontalAlignment="Center" ></TextBlock>
</Grid>
Grid.Row="1" is News Viewing zone (yellow line)
But Storyboard ignored Grid.RowDefinitions like picture
I want to like this
If you create a Storyboard should work like this:
<Storyboard x:Key="NewsFlow">
<ThicknessAnimationUsingKeyFrames Storyboard.TargetProperty="Margin"
Storyboard.TargetName="TextBlock"
RepeatBehavior="Forever" AutoReverse="True">
<EasingThicknessKeyFrame KeyTime="0:0:0" Value="0,0,0,150"/>
<EasingThicknessKeyFrame KeyTime="0:0:1" Value="0,0,0,0"/>
<EasingThicknessKeyFrame KeyTime="0:0:2" Value="0, 150,0,0"/>
</ThicknessAnimationUsingKeyFrames>
</Storyboard>
But Margin lagged behind TranslateTransform. And Textblock The other side is broken during the animation is played
Thank you, regards
<Window.Resources>
<Storyboard x:Key="NewsFlow">
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="Height"
Storyboard.TargetName="News"
RepeatBehavior="Forever" AutoReverse="True">
<EasingDoubleKeyFrame KeyTime="0:0:00" Value="-80"/>
<EasingDoubleKeyFrame KeyTime="0:0:02" Value="80"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="100" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Border Grid.Row="1" Background="BlanchedAlmond"/>
<TextBlock Grid.Row="1" x:Name="News" Text="News" FontSize="40" HorizontalAlignment="Center">
<TextBlock.Triggers>
<EventTrigger RoutedEvent="Loaded">
<BeginStoryboard Storyboard="{StaticResource NewsFlow}"/>
</EventTrigger>
</TextBlock.Triggers>
</TextBlock>
</Grid>

StartStoryboard on another control

I have this xaml:
<Window x:Class="StoryboardTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid.Resources>
<Style TargetType="Button">
<Setter Property="RenderTransformOrigin" Value="0.5 0.5"/>
<Setter Property="RenderTransform">
<Setter.Value>
<RotateTransform x:Name="rt" Angle="0"/>
</Setter.Value>
</Setter>
</Style>
</Grid.Resources>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Button Grid.Column="1" Content="Start">
<Button.Triggers>
<EventTrigger RoutedEvent="Button.Click">
<BeginStoryboard>
<Storyboard >
<DoubleAnimation From="0" To="100" Storyboard.TargetName="pbar" Storyboard.TargetProperty="Value" Duration="0:0:6"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Button.Triggers>
</Button>
<Button Grid.Row="1" Content="1" x:Name="btn1">
<Button.Triggers>
<EventTrigger RoutedEvent="Button.Click">
<BeginStoryboard x:Name="bs1">
<Storyboard>
<DoubleAnimation From="0" To="90" Storyboard.TargetProperty="RenderTransform.Angle" Duration="0:0:1" AutoReverse="True"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Button.Triggers>
</Button>
<Button Grid.Row="1" Grid.Column="1" Content="2" x:Name="btn2">
<Button.Triggers>
<EventTrigger RoutedEvent="Button.Click">
<BeginStoryboard x:Name="bs2">
<Storyboard>
<DoubleAnimation From="0" To="90" Storyboard.TargetProperty="RenderTransform.Angle" Duration="0:0:1" AutoReverse="True"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Button.Triggers>
</Button>
<Button Grid.Row="1" Grid.Column="2" Content="3" x:Name="btn3">
<Button.Triggers>
<EventTrigger RoutedEvent="Button.Click">
<BeginStoryboard x:Name="bs3">
<Storyboard>
<DoubleAnimation From="0" To="90" Storyboard.TargetProperty="RenderTransform.Angle" Duration="0:0:1" AutoReverse="True"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Button.Triggers>
</Button>
<ProgressBar x:Name="pbar" Maximum="100" Grid.Row="2" Grid.ColumnSpan="3"/>
</Grid>
</Window>
As you can see there are 4 buttons(Start button - the main one, buttons 1, 2 and 3) and a progress bar. When I click buttons 1,2 or 3 they rotate slightly. My question is how can I trigger that rotations(which are described by respective storyboards) in Start's button EventTrigger? Is it possible to achieve this not writing 3 other storyboards in the Start button but invoking already attached storyboards for buttons 1,2,3 in xaml only?
<Window.Resources>
<Storyboard x:Key="StoryboardName">
<DoubleAnimation From="0" To="90" Storyboard.TargetProperty="RenderTransform.Angle" Storyboard.TargetName="btn1" Duration="0:0:1" AutoReverse="True"/>
<DoubleAnimation From="0" To="90" Storyboard.TargetProperty="RenderTransform.Angle" Storyboard.TargetName="btn2" Duration="0:0:1" AutoReverse="True"/>
<DoubleAnimation From="0" To="90" Storyboard.TargetProperty="RenderTransform.Angle" Storyboard.TargetName="btn3" Duration="0:0:1" AutoReverse="True"/>
</Storyboard>
</Window.Resources>
<Button Grid.Column="1" Content="Start">
<Button.Triggers>
<EventTrigger RoutedEvent="Button.Click">
<BeginStoryboard Storyboard="{StaticResource StoryboardName}"/>
</EventTrigger>
</Button.Triggers>
</Button>
Doubleanimations declared in storyboard will run simultaneously.
<Button>
<Button.Style>
<Style TargetType="Button">
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=start, Path=IsPressed}" Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard>
...
</BeginStoryboard>
</DataTrigger.EnterActions>
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>

How to rotate Grid background image in WPF?

I want to rotate Grid background image in WPF. I have animation code for image rotation. But when I implement in grid background, Image not accepted so Imagebrush only accepted.
<Grid.Background>
<ImageBrush ImageSource="../Images/1.jpg" Stretch="UniformToFill" TileMode="Tile"/>
</Grid.Background>
I can't implemented below animation code in WPF.
<Canvas ClipToBounds="True" >
<Image Source="/Images/1.jpg" Width="600" >
<Image.RenderTransform>
<RotateTransform x:Name="TransRotate" />
</Image.RenderTransform>
<Image.Triggers>
<EventTrigger RoutedEvent="Image.Loaded">
<BeginStoryboard>
<Storyboard TargetProperty="Angle">
<DoubleAnimation Storyboard.TargetName="TransRotate" Storyboard.TargetProperty="Angle" By="40" Duration="0:0:10" AutoReverse="True" RepeatBehavior="Forever" />
<DoubleAnimation Storyboard.TargetName="TransRotate" Storyboard.TargetProperty="Angle" By="-40" Duration="0:0:15" AutoReverse="True" RepeatBehavior="Forever" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Image.Triggers>
</Image>
</Canvas>
EDIT 1-
If I set the images as a content in grid, row 0 only show the image animation.
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Canvas ClipToBounds="True" >
<Image Name="logo" Source="/Images/1.jpg" Width="800" >
<Image.RenderTransform>
<RotateTransform x:Name="TransRotate" />
</Image.RenderTransform>
<Image.Triggers>
<EventTrigger RoutedEvent="Image.Loaded">
<BeginStoryboard>
<Storyboard TargetProperty="Angle">
<DoubleAnimation Storyboard.TargetName="TransRotate" Storyboard.TargetProperty="Angle" By="40" Duration="0:0:10" AutoReverse="True" RepeatBehavior="Forever" />
<DoubleAnimation Storyboard.TargetName="TransRotate" Storyboard.TargetProperty="Angle" By="-40" Duration="0:0:15" AutoReverse="True" RepeatBehavior="Forever" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Image.Triggers>
</Image>
</Canvas>
<Border Grid.Row="0" Height="180" >
<Image Source="Images/01.jpg" Height="100" />
</Border>
<Border Grid.Row="1" Height="180">
<Image Source="Images/01.jpg" Height="100" />
</Border>
<Border Grid.Row="2" Height="180">
<Image Source="Images/01.jpg" Height="100" />
</Border>
</Grid>
You could rotate an ImageBrush like in the example below. You may also want to set the CenterX and CenterY properties of the RotateTransform.
<Grid>
<Grid.Background>
<ImageBrush ImageSource="C:\Users\Public\Pictures\Sample Pictures\Koala.jpg"
Stretch="UniformToFill">
<ImageBrush.Transform>
<RotateTransform/>
</ImageBrush.Transform>
</ImageBrush>
</Grid.Background>
<Grid.Triggers>
<EventTrigger RoutedEvent="Loaded">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Storyboard.TargetProperty="Background.Transform.Angle"
By="40" Duration="0:0:10"
AutoReverse="True" RepeatBehavior="Forever"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Grid.Triggers>
</Grid>
As you figured out, Grid.Background can only take a Brush. A solution will be to add the Image as a child to the Grid control:
<Grid>
<Image ... />
... grid child elements ...
<Grid>
You will need to keep the Image element as the first child of the grid so that it will appear as grid background, and take care of possible additional problems (like ZOrder of the child elements, number of Grid row / columns, etc), but you should be able to obtain the effect you want.

Categories