Animating Image in MVVM - wpf

I have a view of a viewmodel as defined below
<DataTemplate DataType="{x:Type local:TestViewModel}">
<Grid>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" >
<Button Name="btnStart" Command="{Binding Start}" Margin="5,0,5,0" />
<Button Name="btnCancel" Command="{Binding Cancel}" Margin="5,0,5,0" />
</StackPanel>
</Grid>
</DataTemplate>
So when i click on startButton , I want to start an animation i.e i want to move an arrow image from top of my screent to bottom.
I tried defining the image in grid, defining storyboard in style and attaching datatrigger to property .But this is causing the whole ui to expand rather than the animation moving over the screen.
I want the animation to move over my stackPanel. How do i do that?

Please try the next animation example(it used the TranslateTransform of the Image):
<Grid x:Name="AnimationAreaRootGrid">
<Image x:Name="Image" HorizontalAlignment="Left" VerticalAlignment="Top" Source="Res/Koala.jpg" Width="50" Height="50" RenderTransformOrigin="1.0,1.0">
<Image.RenderTransform>
<TranslateTransform />
</Image.RenderTransform>
</Image>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" VerticalAlignment="Bottom" >
<Button Name="btnStart" Content="Animate Down" Margin="5,0,5,0">
<Button.Resources>
<Storyboard x:Key="DownMovingStoryBoardKey" Storyboard.TargetName="Image">
<!--From - the animation start point-->
<!--From - the animation end point-->
<DoubleAnimation Storyboard.TargetProperty="(Control.RenderTransform).(TranslateTransform.Y)"
From="0"
To="250"
Duration="0:0:1" />
</Storyboard>
</Button.Resources>
<Button.Triggers>
<EventTrigger RoutedEvent="Button.Click">
<BeginStoryboard Storyboard="{StaticResource DownMovingStoryBoardKey}"/>
</EventTrigger>
</Button.Triggers>
</Button>
<Button Name="btnCancel" Margin="5,0,5,0" Content="Animate Up">
<Button.Resources>
<Storyboard x:Key="UpMovingStoryBoardKey" Storyboard.TargetName="Image">
<!--From - the animation start point-->
<!--From - the animation end point-->
<DoubleAnimation Storyboard.TargetProperty="(Control.RenderTransform).(TranslateTransform.Y)"
From="250"
To="0"
Duration="0:0:1" />
</Storyboard>
</Button.Resources>
<Button.Triggers>
<EventTrigger RoutedEvent="Button.Click">
<BeginStoryboard Storyboard="{StaticResource UpMovingStoryBoardKey}"/>
</EventTrigger>
</Button.Triggers>
</Button>
</StackPanel>
</Grid>
Explanations
A clicking on a btnStart will trigger the Down moving animation so the image will be moved down.
A clicking on a btnCancel will trigger the Up moving animation so the image will be moved up.
Regards.

I would use a Canvas and have your Image animated by changing Canvas.Left/Canvas.Top -values.
Canvas, unlike StackPanel, is not affected by it's children when it is doing it's own layout. The downside is that you would have to locate your StackPanel's coordinates, if the animation by the StackPanel coordinates is a requirement.
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" >
<Canvas>
<Image ... />
</Canvas>
<Button Name="btnStart" Command="{Binding Start}" Margin="5,0,5,0" ></Button>
<Button Name="btnCancel" Command="{Binding Cancel}" Margin="5,0,5,0"></Button>
</StackPanel>

How about using a TranslateTransform to animate your Image? Use it with the UIElement.RenderTransform property and you won't affect the layout of the rest of your view.
More infos on WPF transforms:
https://msdn.microsoft.com/en-us/library/ms750596(v=vs.100).aspx
General overview for WPF animations:
https://msdn.microsoft.com/en-us/library/ms752312(v=vs.100).aspx

Related

How can I to set animation of opening popupbox in material design in xmal?

I have a material design Popupbox in my project, And the opening animation of this Popupbox is shown in the GIF below:
But I want the opening animation of this Popup box to be in the form of a GIF below:
Do I have any chance to implement this change?
I have no idea how to implement this change and this is my Popupbox cod in my project:
<materialDesign:PopupBox x:Name="P" PopupUniformCornerRadius="5" PlacementMode="LeftAndAlignMiddles" HorizontalAlignment="Right" Height="34" Margin="0,22,10,0" VerticalAlignment="Center">
<StackPanel>
<Grid Height="60" Width="90">
<ToggleButton
HorizontalAlignment="Right"
Style="{StaticResource MaterialDesignSwitchToggleButton}"
ToolTip="Lock/Unlock" Cursor="Hand">
<materialDesign:ToggleButtonAssist.SwitchTrackOnBackground>
<SolidColorBrush
Color="DarkSlateGray" />
</materialDesign:ToggleButtonAssist.SwitchTrackOnBackground>
<materialDesign:ToggleButtonAssist.SwitchTrackOffBackground>
<SolidColorBrush
Color="Gray" />
</materialDesign:ToggleButtonAssist.SwitchTrackOffBackground>
</ToggleButton>
</Grid>
<Button Content="2"/>
<Button Content="3"/>
</StackPanel>
</materialDesign:PopupBox>
The Popup animation is implemented in the WPF Popup class and is not specific to the MaterialDeisgnThemes library (See PopupAnimation). There are four different animation modes that you can set (None, Fade, Slide and scroll). The PopupBox-Style sets the mode to "fade". Unfortunately, none of them will animate the Popup the way you want it.
The only solution I can think of, is to animate the popup content yourself. Since the popup is basically a window that fits its size to its content, you need to animate the LayoutTransfrom to scale or translate the popup over time. Here is an example that does more or less what you need:
<mat:PopupBox HorizontalAlignment="Center"
VerticalAlignment="Center"
PlacementMode="LeftAndAlignTopEdges"
PopupMode="Click">
<Grid>
<Grid.LayoutTransform>
<TransformGroup>
<ScaleTransform />
</TransformGroup>
</Grid.LayoutTransform>
<StackPanel>
<Grid Width="90"
Height="60">
<ToggleButton HorizontalAlignment="Right"
Cursor="Hand"
Style="{StaticResource MaterialDesignSwitchToggleButton}"
ToolTip="Lock/Unlock">
<mat:ToggleButtonAssist.SwitchTrackOnBackground>
<SolidColorBrush Color="DarkSlateGray" />
</mat:ToggleButtonAssist.SwitchTrackOnBackground>
<mat:ToggleButtonAssist.SwitchTrackOffBackground>
<SolidColorBrush Color="Gray" />
</mat:ToggleButtonAssist.SwitchTrackOffBackground>
</ToggleButton>
</Grid>
<Button Content="2" />
<Button Content="3" />
</StackPanel>
<Grid.Triggers>
<EventTrigger RoutedEvent="Loaded">
<BeginStoryboard>
<Storyboard>
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(FrameworkElement.LayoutTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleX)">
<EasingDoubleKeyFrame KeyTime="0:0:0:0"
Value="0" />
<EasingDoubleKeyFrame KeyTime="0:0:0:0.1"
Value="1" />
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(FrameworkElement.LayoutTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleY)">
<EasingDoubleKeyFrame KeyTime="0:0:0:0"
Value="0" />
<EasingDoubleKeyFrame KeyTime="0:0:0:0.1"
Value="1" />
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Grid.Triggers>
</Grid>
</mat:PopupBox>

WPF access name of Usercontrol button from parent

I am trying to trigger a storyboard animation when the click event of a button in a usercontrol
is clicked. The parent doesn't recognise the name of the button within the usercontrol
ie trying UserControl.MyButton as SourceName doesn't work. How can I access the name of the button and use in the SourceName for the EventTrigger
Here is the pseudo code of main window
<Window>
<EventTrigger RoutedEvent="Button.Click" SourceName="???" >
<BeginStoryboard >
-----------------------
</BeginStoryboard>
</EventTrigger>
<UserControl />
</Window>
Here is the user control defined in separate xaml
<UserControl>
<Button x:Name="MyButton" />
</UserControl>
Actually, it seems that SourceName does not have to be an element in the current namescope. The following code works for Button1 and not for Button2. EvenTrigger should be checking the original source name when handling the event:
<StackPanel>
<StackPanel.Triggers>
<EventTrigger RoutedEvent="Button.Click" SourceName="Button1">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation To="30" Storyboard.TargetName="TextBlock1" Storyboard.TargetProperty="FontSize" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</StackPanel.Triggers>
<TextBlock x:Name="TextBlock1" Text="Text" FontSize="12" />
<local:UserControl1 x:Name="UC" />
</StackPanel>
<UserControl>
<StackPanel>
<Button x:Name="Button1" Content="Click"/>
<Button x:Name="Button2" Content="Click"/>
</StackPanel>
</UserControl>

Expander getting stuck after IsExpanded set to true by storyboard

So I have an expander that I want to have the normal functionality (open and close with its own button) but I also want a different button to expand it when pressed (this button is in the header of the expander). I'm using a storyboard in an event trigger for the Button.Click which works, but after it is expanded this way the normal button doesn't work, it just stays expanded. My xaml is below, I would really prefer to keep this all in the xaml, I could come up with a way to do it in the codebehind/viewmodel myself.
<Expander x:Name="IndexExpander" IsExpanded="True" Grid.Row="4" Grid.ColumnSpan="5" Margin="10" MaxHeight="150">
<Expander.Triggers>
<EventTrigger SourceName="btnAddIndex" RoutedEvent="Button.Click">
<BeginStoryboard>
<Storyboard>
<BooleanAnimationUsingKeyFrames Storyboard.TargetName="IndexExpander" Storyboard.TargetProperty="IsExpanded" BeginTime="0:0:0.25" Duration="0:0:0.20" >
<DiscreteBooleanKeyFrame KeyTime="0" Value="True" />
</BooleanAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Expander.Triggers>
<Expander.Header>
<StackPanel DockPanel.Dock="Left" Orientation="Horizontal">
<TextBlock Text="Indexes" FontWeight="Bold"/>
<!-- Add/Delete Buttons-->
<Grid Margin="10,0,0,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="50*"/>
<ColumnDefinition Width="50*"/>
</Grid.ColumnDefinitions>
<Button Grid.Column="0" x:Name="btnAddIndex" Command="{Binding AddIndexCommand}" Template="{StaticResource AddButtonTemplate}" IsEnabled="{Binding IsEditable}" Margin="0,0,5,0" />
</Grid>
</StackPanel>
</Expander.Header>
Alright, so for anyone following in my footsteps here's what I did. I got the idea from here, and adapted it until it worked correctly.
<Expander.Triggers>
<EventTrigger SourceName="btnAddCol" RoutedEvent="Button.Click">
<BeginStoryboard x:Name="ColumnExpanderStory">
<Storyboard>
<BooleanAnimationUsingKeyFrames Storyboard.TargetName="ColumnExpander" Storyboard.TargetProperty="IsExpanded">
<DiscreteBooleanKeyFrame KeyTime="0" Value="True" />
</BooleanAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="ToggleButton.PreviewMouseUp">
<RemoveStoryboard BeginStoryboardName="ColumnExpanderStory" />
</EventTrigger>
</Expander.Triggers>
<Expander.Header>
The problem was that the storyboard overrides any other bindings to the IsExpanded property, so it has to be removed to restore them (read more here). The suggestion was to use the ToggleButton.Checked event to remove the storyboard, but that didn't work for me, only the "Preview" events seemed to have the right timing. I started with PreviewMouseDown, but it would remove the storyboard, then on mouse up toggle the expander, meaning the first click would just flip states back and forth quickly. Using PreviewMouseUp got around that issue.

Access ItemsControl Items and Animate One by One

Today is a good day since I started with WPF, this for a launcher I'm creating.
Using the following code, I managed to get the result to be seen in the screenshot:
<Grid>
<ItemsControl ItemsSource="{Binding Programs}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal" VerticalAlignment="Center" HorizontalAlignment="Center" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Content="{Binding Text}" Background="Transparent" Foreground="White" Width="128" Height="150" >
<Button.RenderTransform>
<TransformGroup>
<ScaleTransform />
</TransformGroup>
</Button.RenderTransform>
<Button.Template>
<ControlTemplate TargetType="Button">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Image Grid.Row="0" Source="{Binding Image}" Height="128" />
<ContentPresenter Grid.Row="1" HorizontalAlignment="Center" Margin="3,10" />
<Rectangle Grid.Row="0" Fill="{TemplateBinding Background}" />
<Rectangle Grid.Row="1" Fill="{TemplateBinding Background}" />
</Grid>
</ControlTemplate>
</Button.Template>
<Button.Resources>
<Storyboard SpeedRatio="4" x:Key="MouseEnterStoryboard" x:Name="MouseEnterStoryboard">
<ColorAnimation Storyboard.TargetProperty="(Button.Background).(SolidColorBrush.Color)" To="#22FFFFFF"></ColorAnimation>
</Storyboard>
<Storyboard SpeedRatio="4" x:Key="MouseLeaveStoryboard" x:Name="MouseLeaveStoryboard">
<ColorAnimation Storyboard.TargetProperty="(Button.Background).(SolidColorBrush.Color)" To="Transparent"></ColorAnimation>
</Storyboard>
<Storyboard Duration="00:00:00.05" x:Key="MouseClickStoryboard" AutoReverse="True">
<DoubleAnimation To="0.8" Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleX)"/>
<DoubleAnimation To="0.8" Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleY)"/>
</Storyboard>
<Storyboard x:Key="WindowLoadedStoryboard">
<DoubleAnimation Storyboard.TargetProperty="Opacity" From="0" To="1" Duration="00:00:01" />
</Storyboard>
</Button.Resources>
<Button.Triggers>
<EventTrigger RoutedEvent="Mouse.MouseEnter">
<BeginStoryboard Storyboard="{StaticResource MouseEnterStoryboard}" />
</EventTrigger>
<EventTrigger RoutedEvent="Mouse.MouseLeave">
<BeginStoryboard Storyboard="{StaticResource MouseLeaveStoryboard}" />
</EventTrigger>
<EventTrigger RoutedEvent="Button.Click">
<BeginStoryboard Storyboard="{StaticResource MouseClickStoryboard}" />
</EventTrigger>
<EventTrigger RoutedEvent="Window.Loaded">
<BeginStoryboard Storyboard="{StaticResource WindowLoadedStoryboard}"></BeginStoryboard>
</EventTrigger>
</Button.Triggers>
</Button>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
Screenshot:
Now, for each item in the list bound to this control, it will create a button.
How would I access this button programmatically, better yet, how would I access one of the Storyboards programatically since assigning a name (x:to them simply won't do the trick it seems...
Also, how can I animate the buttons one by one? Currently they each fade in at exact the same time (# WindowLoadedStoryboard), but I would like to let each button fade in one by one with a short delay, to create a nice effect. How would I achieve this?
Hope someone can answer these 2 questions for me!
Greetings!
Your problem with accessing the elements defined in the DataTemplate is caused because you defined those elements in a DataTemplate... those elements could be rendered in many different types of UI container controls. You can find the solution in the How to: Find DataTemplate-Generated Elements page from MSDN.
You first need to get hold of the relevant container control that contains the item that has had that DataTemplate applied to it. Next, you need to get the ContentPresenter from that container control and then you can get the DataTemplate from ContentPresenter. Finally, you can access the named elements from the DataTemplate. From the linked page:
// Getting the currently selected ListBoxItem
// Note that the ListBox must have
// IsSynchronizedWithCurrentItem set to True for this to work
ListBoxItem myListBoxItem = (ListBoxItem)(myListBox.ItemContainerGenerator.
ContainerFromItem(myListBox.Items.CurrentItem));
// Getting the ContentPresenter of myListBoxItem
ContentPresenter myContentPresenter = FindVisualChild<ContentPresenter>(myListBoxItem);
// Finding textBlock from the DataTemplate that is set on that ContentPresenter
DataTemplate myDataTemplate = myContentPresenter.ContentTemplate;
TextBlock myTextBlock =
(TextBlock)myDataTemplate.FindName("textBlock", myContentPresenter);
// Do something to the DataTemplate-generated TextBlock
MessageBox.Show("The text of the TextBlock of the selected list item: " +
myTextBlock.Text);

Animation from within DataTemplate

Below is the data template I'm using for a listbox's ItemTemplate. It shows some simple data, and a button, which ideally should animate a Popup, also contained within the DataTemplate. Unfortunately the whole thing blow up at runtime. The error says line 52 is wrong, which is:
<EventTrigger RoutedEvent="Button.Click">
Here's the whole DataTemplate. I've used this popup before, with the same exact content templates and even the same animation elsewhere. It's only blowing up when I try to put it into a DataTemplate. I assume the animation is having difficulty finding the right animation target - I'm hoping someone here would know more.
<DataTemplate x:Key="ItemTemplate2">
<Border Width="100" Height="100" BorderThickness="4" BorderBrush="Red">
<StackPanel>
<TextBlock Text="{Binding Name}"/>
<TextBlock Text="{Binding Price}"/>
<Popup x:Name="popupContent" IsOpen="True" Margin="10,0,0,0" Grid.Row="0" >
<Popup.Child>
<Thumb x:Name="thumbContent" DragDelta="Thumb_DragDelta" Width="0" Height="0">
<Thumb.Template>
<ControlTemplate>
<local:thumbTemplate Margin="0" x:Name="df" />
</ControlTemplate>
</Thumb.Template>
</Thumb>
</Popup.Child>
</Popup>
<Button Content="Show">
<Button.Triggers>
<EventTrigger RoutedEvent="Button.Click">
<BeginStoryboard>
<BeginStoryboard.Storyboard>
<Storyboard x:Name="sbShowPopup">
<DoubleAnimation Duration="0:0:1" To="200" Storyboard.TargetProperty="(FrameworkElement.Width)" Storyboard.TargetName="popupContent" d:IsOptimized="True"/>
<DoubleAnimation Duration="0:0:1" To="80" Storyboard.TargetProperty="(FrameworkElement.Height)" Storyboard.TargetName="popupContent" d:IsOptimized="True"/>
</Storyboard>
</BeginStoryboard.Storyboard>
</BeginStoryboard>
</EventTrigger>
</Button.Triggers>
</Button>
</StackPanel>
</Border>
</DataTemplate>
You may have used this before but not in Silverlight. The only supported value for RoutedEvent in Silverlight is "FrameworkElement.LoadedEvent".
You will need the BlendSDK to do this sort of thing in Silverlight.

Resources