Basic Animation in WPF: How to animate a grid when expanding - c#

I am using MVVm in my WPF application. I am currently playing around with animating a grid so that when a user selected a particular item in a combobox I want my grid to animate as it expand. I have a property in my View Model that I want to bind to trigger the animation in my grid. How do we do this in MVVM? So far I have recently found a solution using
EventTrigger RoutedEvent ="SampleGridEventName"
is there any way to use other triggers to bind to a property in my ViewModel?Something like
Trigger BlahBlah ="{Binding ExpandGrid}"

Yes. Style has DataTrigger which also has EnterActions property. Animations are stored in resources. That's the trick.
<UserControl x:Class="TriggerSpike.UserControl1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Height="300" Width="300">
<UserControl.Resources>
<DoubleAnimation x:Key="SearchAnimation" Storyboard.TargetProperty="Opacity" To="1" Duration="0:0:4"/>
<DoubleAnimation x:Key="StopSearchAnimation" Storyboard.TargetProperty="Opacity" To="0" Duration="0:0:4"/>
</UserControl.Resources>
<StackPanel>
<TextBlock Name="progressWheel" TextAlignment="Center" Opacity="0">
<TextBlock.Style>
<Style>
<Style.Triggers>
<DataTrigger Binding="{Binding IsBusy}" Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<StaticResource ResourceKey="SearchAnimation"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<StaticResource ResourceKey="StopSearchAnimation"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.ExitActions>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
Searching
</TextBlock>
<Label Content="Here your search query"/>
<TextBox Text="{Binding SearchClause}"/>
<Button Click="Button_Click">Search!</Button>
<TextBlock Text="{Binding Result}"/>
</StackPanel>
</UserControl>

Related

WPF XAML Togglebutton trigger event on different target

I've trying to create a navigation panel on the left of my app, I have several ListViews stacked on top of each other, with an invisible ToggleButton on the first item of each list. Then when these ToggleButtons are switched I want to trigger an event on the other ListViews.
I.e. change the height of all other ListViews to just view the first item;
Setter TargetName="ClientItems" Property="Height" Value="40"
And also switch the value of the other ToggleButtons so only one or none ToggleButtons are checked.
Setter TargetName="ClientsTglBtn" Value="ToggleButton.Checked"
<ToggleButton x:Name="ProjectsTglBtn"
Width="190"
Height="40"
VerticalAlignment="Top"
BorderThickness="0"
Background="Transparent"
Style="{StaticResource ToggleButtonStyle}">
<ToggleButton.Triggers>
<EventTrigger RoutedEvent="ToggleButton.Unchecked">
<BeginStoryboard>
<Storyboard x:Name="HideProjectItems">
<DoubleAnimation
Storyboard.TargetName="ProjectItems"
Storyboard.TargetProperty="Height"
BeginTime="0:0:0:0"
From="160" To="40"
Duration="0:0:0:0.25">
</DoubleAnimation>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="ToggleButton.Checked">
<Setter TargetName="ClientItems" Property="Height" Value="40"/>
<Setter TargetName="ClientsTglBtn" Value="ToggleButton.Checked"/>
<BeginStoryboard>
<Storyboard x:Name="ShowProjectItems">
<DoubleAnimation
Storyboard.TargetName="ProjectItems"
Storyboard.TargetProperty="Height"
BeginTime="0:0:0:0"
From="40" To="160"
Duration="0:0:0:0.25">
</DoubleAnimation>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</ToggleButton.Triggers>
</ToggleButton>

Adding animations to DataTemplate-Generated controls

I have some problem with adding animations to DataTemplate-Generated controls. I want to animate Border height.
XAML code:
<StackPanel x:Name="stackpanel">
<ItemsControl ItemsSource="{Binding}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border Height="42">
<!--There some controls-->
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
There C# code(but it works for only last one item):
StoryBoard maxhasb = new Storyboard();
StoryBoard minhasb = new Storyboard();
var maximizeHeightAnimation= new DoubleAnimation(42, 72, duration, FillBehavior.HoldEnd);
var minimizeHeightAnimation= new DoubleAnimation(72, 42, duration, FillBehavior.HoldEnd);
....
ItemsControl itemsControl = (ItemsControl)stackpanel.Children[0];
foreach (var item in itemsControl.Items)
{
ContentPresenter contentPresenter = (ContentPresenter)itemsControl.ItemContainerGenerator.ContainerFromItem(item);
Border border = (Border)itemsControl.ContentTemplate.LoadContent();
Storyboard.SetTarget(maximizeHeightAnimation, border);
Storyboard.SetTargetProperty(maximizeHeightAnimation, new PropertyPath(HeightProperty));
Storyboard.SetTarget(minimizeHeightAnimation, border);
Storyboard.SetTargetProperty(minimizeHeightAnimation, new PropertyPath(HeightProperty));
maxhasb.Children.Add(maximizeHeightAnimation);
minhasb.Children.Add(minimizeHeightAnimation);
}
Is there anything that I'm doing wrong?
It's quite difficult to work out what you intend doing with this. I guess the idea is to increase the height of an item and then decrease it a bit as it's inserted.
Maybe that isn't exactly what you intend.
You might find the sample for the "Toast" in this article interesting:
https://social.technet.microsoft.com/wiki/contents/articles/31416.wpf-mvvm-friendly-user-notification.aspx#Toast
You probably just want to use a layouttransform to animate ScaleY up and then down without any bounce. But maybe bounce will give you the effect you want or you're not bothered about the specific effect.
From ToastList:
<ListBox ItemsSource="{Binding Messages}" BorderBrush="Transparent" Background="LightGray">
<ListBox.ItemContainerStyle>
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBoxItem}">
<ContentPresenter/>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="LayoutTransform">
<Setter.Value>
<ScaleTransform/>
</Setter.Value>
</Setter>
<Style.Triggers>
<EventTrigger RoutedEvent="Loaded">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Opacity" From="0" To="1" Duration="0:0:1.2" FillBehavior="Stop" />
<DoubleAnimation Storyboard.TargetProperty="LayoutTransform.ScaleY" From="0" Duration="0:0:.2" FillBehavior="Stop">
<DoubleAnimation.EasingFunction>
<BounceEase Bounces="2" Bounciness="6"/>
</DoubleAnimation.EasingFunction>
</DoubleAnimation>
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
<DataTrigger Binding="{Binding IsGoing}" Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Opacity" From="1" To="0" Duration="0:0:1.2" />
<DoubleAnimation Storyboard.TargetProperty="LayoutTransform.ScaleY" To="0" Duration="0:0:1.2"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
</DataTrigger>
</Style.Triggers>
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate>
<Border Background="Yellow"
CornerRadius="3"
BorderThickness="1"
Margin="4" Padding="4">
<Border.BitmapEffect>
<DropShadowBitmapEffect ShadowDepth="4" Color="Purple" />
</Border.BitmapEffect>
<TextBlock Text="{Binding Msg}" FontWeight="SemiBold" TextWrapping="Wrap" MaxWidth="200"/>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>

Toggle opacity of control

I have button, which should toggle Opacity of element. Currently it works only at half - on click make control visible( set opacity to 1). I've tried use ToogleButton instead of simple Button to use properties IsChechecked (I think it is the same as IsClicked) but what I found wasn't good for me, cause for this properties I can't set TargetName for animation.
<ToggleButton BorderThickness="0" Background="Transparent" Grid.Column="0" Padding="0" Grid.Row="0" Focusable="False" Width="Auto" PreviewMouseUp="Share_Click">
<ToggleButton.Content>
<Grid HorizontalAlignment="Stretch">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<TextBlock Margin="0,0,6,0" Grid.Column="0" Text="Filter" Foreground="#FFC0B6D1" FontFamily="Segoe UI Semibold" FontSize="14" />
<Image Grid.Column="1" Margin="0,4,0,0" VerticalAlignment="Center" Height="6" Width="12" Name="FiltersToggleArrow" Style="{StaticResource ToogleFilters}"/>
</Grid>
</ToggleButton.Content>
<ToggleButton.Triggers>
<EventTrigger RoutedEvent="UIElement.PreviewMouseDown">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation BeginTime="0:0:0.00" Storyboard.TargetName="FiltersPanel" Storyboard.TargetProperty="Opacity" From="0" To="1" Duration="0:0:00.2" AutoReverse="False"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</ToggleButton.Triggers>
</ToggleButton>
The MSDN documentation says:
You can set this property to the name of any element within the scope of where the setter collection (the collection that this setter is part of) is applied. This is typically a named element that is within the template that contains this setter.
To get around this, you can instead apply your Storyboard inside the Style of your Panel, not the ToggleButton. In this case, you can use a DataTrigger to bind to the IsChecked property and act accordingly.
Here's an example I did with a ToggleButton and a Rectangle.
<ToggleButton Content="Switch" Name="chk"/>
<Rectangle Fill="Red" Name="rect"
Width="50" Height="50">
<Rectangle.Style>
<Style TargetType="Rectangle">
<Style.Triggers>
<DataTrigger Binding="{Binding IsChecked, ElementName=chk}" Value="False">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard
Storyboard.TargetProperty="Opacity">
<DoubleAnimation From="1"
To="0"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<BeginStoryboard>
<Storyboard
Storyboard.TargetProperty="Opacity">
<DoubleAnimation From="0"
To="1"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.ExitActions>
</DataTrigger>
</Style.Triggers>
</Style>
</Rectangle.Style>
</Rectangle>
From my experience with triggers, they can be a royal pain in the backside and are tricky to get right. That being said, this example should be enough to fit your requirements.

WPF Popup follow mouse in DataTemplate

I'm fighting with making popup follow mouse within DataTemplate
I have following DataTemplate:
<DataTemplate>
<StackPanel x:Name="MyPanel">
<Popup x:Name="MyPopup" Visibility="Visible" AllowsTransparency="True" Placement="MousePoint">
something here
</Popup>
<controls1:ImageLoader x:Name="MyImage" Margin="10,10,0,0" Source="{Binding ImageUri}" Width="100" Height="100">
</controls1:ImageLoader>
</StackPanel>
<DataTemplate.Triggers>
<EventTrigger RoutedEvent="MyImage.MouseEnter" >
<BeginStoryboard>
<Storyboard>
<BooleanAnimationUsingKeyFrames
Storyboard.TargetName="MyPopup"
Storyboard.TargetProperty="IsOpen"
Duration="0:0:0.5">
<DiscreteBooleanKeyFrame Value="True" KeyTime="100%"/>
</BooleanAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="MyPanel.MouseLeave" >
<BeginStoryboard>
<Storyboard>
<BooleanAnimationUsingKeyFrames
Storyboard.TargetName="MyPopup"
Storyboard.TargetProperty="IsOpen"
Duration="0:0:0.01">
<DiscreteBooleanKeyFrame Value="False" KeyTime="100%"/>
</BooleanAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</DataTemplate.Triggers>
</DataTemplate>
Right now popup is working : I mean it shows after 500ms and hides (almost correctly)
I'm trying to make a popup follows my mouse pointer while moving around MyImage control.
How to do it?

WPF how to animate picture inside listboxitem using data trigger?

I have a listbox with templated listboxitem which is binded to an object.
one of the listboxitem controls is an image and it's source is also binded to a property inside the datacontext, So the pictur inside the Image control is different according to that property.
I want to animate the image but only specific one (when the binded property value is some specific value only).
I guess there are other ways to do that, maybe using code behind. But I would realy like use it in the xaml code, so I thought using animation inside DataTrigger, it makes sense to me because the starting/ending the animation depends on the DataContext:
<DataTrigger Binding="{Binding Path=Value.SomeProperty}"
Value="SomePropertyValue">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard TargetName="SomePropertyIcon">
<StaticResource ResourceKey="SomePropertyValueAnimation"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<BeginStoryboard>
<Storyboard TargetName="SomePropertyIcon">
<StaticResource ResourceKey="StopSomePropertyValueAnimation"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.ExitActions>
</DataTrigger>
But I cannot make it work any way.
I tried to put that code inside a style of the listBox and then apply that style to the image, it didnt work, I also tried to put inside the listBox.ItemTamplate, and other places but nothing helped.
Here is the code. maybe it can help to understand (I pasted only relevan code here):
<UserControl ...>
<UserControl.Resources>
<local:SomePropertyToImageConverter x:Key="somePropertyToImageConverter"/>
<DoubleAnimation x:Key="SomePropertyValueAnimation" Storyboard.TargetProperty="(UIElement.RenderTransform).(RotateTransform.Angle)" To="45" Duration="0:0:2" RepeatBehavior="Forever"/>
<DoubleAnimation x:Key="StopSomePropertyValueAnimation" Storyboard.TargetProperty="(UIElement.RenderTransform).(RotateTransform.Angle)" To="0" Duration="0:0:2"/>
</UserControl.Resources>
<Grid>
<ListBox Name="myListBox"
ItemsSource="{Binding Path=myDataContext}" IsEnabled="True" Focusable="True" SelectionMode="Extended">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Image Width="20" Height="20" Name="SomePropertyIcon"
Source="{Binding Path=Value.SomeProperty, Converter={StaticResource somePropertyToImageConverter}}"/>
</Grid>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding Path=Value.SomeProperty}"
Value="SomePropertyValue">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard TargetName="SomePropertyIcon">
<StaticResource ResourceKey="SomePropertyValueAnimation"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<BeginStoryboard>
<Storyboard TargetName="SomePropertyIcon">
<StaticResource ResourceKey="StopSomePropertyValueAnimation"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.ExitActions>
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</ListBox.ItemTemplate>
<ListBox.ItemContainerStyle>
<Style TargetType="{x:Type ListBoxItem}">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=Value.SomeProperty}"
Value="SomePropertyValue">
<Setter Property="Background" Value="Bisque"/>
</DataTrigger>
</Style.Triggers>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
</Grid>
Animation cannot change property values for a property path that does not exist. The "Image" you are trying to manipulate does not have "Rotate transform" you should add it and it should work:
<Image Width="20" Height="20" Name="SomePropertyIcon"
Source="{Binding Path=Value.SomeProperty, Converter={StaticResource somePropertyToImageConverter}}">
<Image.RenderTransform >
<RotateTransform />
</Image.RenderTransform>
</Image>

Categories

Resources