I am currently having an annoying issue with the simple XAML code below. The animation does fire correctly, and will always fire correctly if I click the button after the animation has completed. If I click quickly a few times, the animation will freeze, and the animation will never run again.
Thanks so much for the help!
<!-- Admin Login Button /-->
<ToggleButton Name="LoginButton" IsChecked="{Binding ElementName=LoginPopup, Path=IsOpen, Mode=TwoWay}" HorizontalAlignment="Right" Margin="0,35,32.334,0" VerticalAlignment="Top" Width="97" Height="77" BorderThickness="0" Grid.Column="1" Grid.ColumnSpan="2">
<ToggleButton.Background>
<ImageBrush ImageSource="/Resources/Images;component/Resources/titleAboutIcon.png"/>
</ToggleButton.Background>
<!-- <Frame x:Name="AdminFrame" Height="77" Width="97"/> /-->
<ToggleButton.RenderTransform>
<ScaleTransform x:Name="Buttonscale" ScaleX="1" ScaleY="1" CenterX="0" CenterY="{Binding ElementName=LoginButton, Path=ActualHeight}" />
</ToggleButton.RenderTransform>
<ToggleButton.Triggers>
<EventTrigger RoutedEvent="Button.Click">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetName="Buttonscale" Storyboard.TargetProperty="(ScaleTransform.ScaleX)" To="1.25" Duration="0:0:0.25" AutoReverse="True"/>
<DoubleAnimation Storyboard.TargetName="Buttonscale" Storyboard.TargetProperty="(ScaleTransform.ScaleY)" To="1.25" Duration="0:0:0.25" AutoReverse="True"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</ToggleButton.Triggers>
</ToggleButton>
<!-- Admin Login Button /-->
You can add FillBehavior="Stop" to your Storyboard so it will stop and reset animated properties values instead of freezing them (HoldEnd is the default value of FillBehavior, so it won't change values back to their default).
From MSDN:
Set an animations FillBehavior property to HoldEnd when you want the animation to hold its value after it reaches the end of its active period. An animation that has reached the end of its active period that has a FillBehavior setting of HoldEnd is said to be in its fill period. When you don't want an animation to hold its value after it reaches the end of its active period, set its
FillBehavior property to Stop.
https://msdn.microsoft.com/en-us/library/system.windows.media.animation.timeline.fillbehavior(v=vs.110).aspx
Related
I have a WPF slide notification I want to show in the bottom right corner of the screen. I need to show and hide the notification multiple times from the main program, so I cannot Close() and Show() the window each time. I have to keep doing Hide() and Show().
IsHitTestVisible is set to False so that I can click anywhere on the window to dismiss and hide the notification and bring another window to the foreground.
The very first time I execute the window animation, it runs perfectly. If I let the window animation run all the way to the end, then the next time the slide notification is shown, the animation is displayed perfectly.
The problem comes in if I click on the window before the animation has run it's full course. Clicking on the window hides the window and then the storyboard is set to SkipStoryboardtoFill and FillBehavior is set to Stop so that the animation clocks get reset to their original values. However, the next time the hidden notification window is shown, it seems to remember the last window position the window had before it was hidden, and then starts the animation. So the animation looks really strange. I am trying to figure out how to set up the animation so that it displays the same way each time without this glitch.
<Window
x:Class="NotificationWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Notification Popup"
Width="480"
Height="140"
WindowStyle="None"
AllowsTransparency="True"
Background="Transparent"
>
<Grid Name="ToastWindowGrid" RenderTransformOrigin="0,1">
<Border Name="ToastWindowBorder" BorderThickness="0" Background="#333333">
<StackPanel Name="ToastWindowStackPanel" Margin="10" Orientation="Horizontal">
<Image Name="ToastLogo" Width="100" Height="100" Source="D:\Development\resources\WindowsLogo-Blue-x100.png"/>
<StackPanel Name="ToastMessageStackPanel" Width="359">
<TextBox Name="ToastTitleTextBox" Margin="5" MaxWidth="340" Background="#333333" BorderThickness="0" IsReadOnly="True" Foreground="White" FontSize="20" Text="Windows 10 Upgrade" FontWeight="Bold" HorizontalContentAlignment="Center" Width="Auto" HorizontalAlignment="Stretch" IsHitTestVisible="False"/>
<TextBox Name="TotastMessageTextBox" Margin="5" MaxWidth="340" Background="#333333" BorderThickness="0" IsReadOnly="True" Foreground="LightGray" FontSize="16" Text="A Windows upgrade is available. Click to upgrade or schedule update." HorizontalContentAlignment="Left" TextWrapping="Wrap" IsHitTestVisible="False"/>
</StackPanel>
</StackPanel>
</Border>
<Grid.Triggers>
<EventTrigger RoutedEvent="FrameworkElement.Loaded">
<BeginStoryboard Name="BeginToastAnimationStoryboard">
<Storyboard Name="ToastAnimationStoryboard" FillBehavior="Stop">
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(ScaleTransform.ScaleY)" FillBehavior="Stop">
<SplineDoubleKeyFrame KeyTime="0:0:0" Value="0"/>
<SplineDoubleKeyFrame KeyTime="0:0:0.5" Value="1"/>
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)" FillBehavior="Stop">
<SplineDoubleKeyFrame KeyTime="0:0:18" Value="1"/>
<SplineDoubleKeyFrame KeyTime="0:0:20" Value="0"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="FrameworkElement.MouseUp">
<SkipStoryboardToFill BeginStoryboardName="BeginToastAnimationStoryboard"/>
<RemoveStoryboard BeginStoryboardName="BeginToastAnimationStoryboard"/>
</EventTrigger>
</Grid.Triggers>
<Grid.RenderTransform>
<ScaleTransform ScaleY="1" />
</Grid.RenderTransform>
</Grid>
With Code Behind:
using System;
using System.Windows;
using System.Windows.Threading;
public partial class NotificationWindow
{
public NotificationWindow()
{
InitializeComponent();
Dispatcher.BeginInvoke(DispatcherPriority.ApplicationIdle, new Action(() =>
{
var workingArea = System.Windows.Forms.Screen.PrimaryScreen.WorkingArea;
var transform = PresentationSource.FromVisual(this).CompositionTarget.TransformFromDevice;
var corner = transform.Transform(new Point(workingArea.Right, workingArea.Bottom));
this.Left = corner.X - this.ActualWidth - 10;
this.Top = corner.Y - this.ActualHeight;
}));
}
}
The first time the notification is shown, I use the Loaded event handler to call the Begin method with isControllable set to True. After that, I use the IsVisibleChanged event handler to start the animation if the Visibility property of the window is set to Visible.
Change the MouseUp EventTrigger to:
Reset to the beginning of the animation, using SeekStoryboard
Stop the animation, using PauseStoryboard. Using StopStoryboard run the animation to the fill state, which is not what you want: you want the window to disappear.
Here is the change in xaml:
<EventTrigger RoutedEvent="FrameworkElement.MouseUp">
<SeekStoryboard BeginStoryboardName="BeginToastAnimationStoryboard" />
<PauseStoryboard BeginStoryboardName="BeginToastAnimationStoryboard" />
</EventTrigger>
Resetting the animation ensures that when the animation is restarted it will start again from the beginning of the storyboard.
I would recommend to create a new instance for each notification. Let me explain why.
You can configure them independently by using a configuration object, for example one can stay visible for 10 seconds, then the rest notifications with a default 5 seconds.
You can show multiple notifications at once.
Every notification being a window will start it's own event loop and thread.
For this to work however you must ensure that the window is very light and can be initialized very fast.
I have a set of Images in a GridView, and the images are not able to load instantly when the page is opened. So, to create a smoother transition, I am trying to use an EventTrigger in the Image to animate the opacity from 0 to 1 when the image loads, like so:
<GridView.ItemTemplate>
<DataTemplate>
<Border Background="{ThemeResource ButtonBackground}" HorizontalAlignment="Stretch" MaxWidth="300" MinWidth="200">
<Image Source="{Binding SmallUri}" Stretch="UniformToFill"
Opacity="0" ToolTipService.ToolTip="{Binding Author.Name}">
<Image.Triggers>
<EventTrigger RoutedEvent="Image.ImageOpened">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Opacity" To="1" Duration="00:00:00.25" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Image.Triggers>
</Image>
</Border>
</DataTemplate>
</GridView.ItemTemplate>
But whenever the page attempts to load, it crashes with the error:
Failed to assign to property 'Windows.UI.Xaml.EventTrigger.RoutedEvent'
Why does this not work? It also fails if I change the RoutedEvent property to Loaded, FrameworkElement.Loaded, Image.Loaded, or any other value I could think of. I would like a solution that does not involve having to write a custom control / codebehind.
But whenever the page attempts to load, it crashes with the error "Failed to assign to property 'Windows.UI.Xaml.EventTrigger.RoutedEvent'." Why does this not work?
As #Clemens said, problem is in Windows Runtime XAML, the default behavior for event triggers and the only event that can be used to invoke an EventTrigger is FrameworkElement.Loaded.
I'm writing this answer here to call another problem of your code, as you said:
It also fails if I change the RoutedEvent property to Loaded, FrameworkElement.Loaded, Image.Loaded
This is because you didn't specify the Storyboard.TargetName in your DoubleAnimation, it will throw the exception when it runs. To solve this problem, you will need to give a name to your Image in the DataTemplate and modify your storyboard like this:
<Image x:Name="myImage" Source="{Binding SmallUri}" Stretch="UniformToFill">
<Image.Triggers>
<EventTrigger RoutedEvent="Image.Loaded">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Opacity" From="0" To="1" Duration="00:00:5" Storyboard.TargetName="myImage" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Image.Triggers>
</Image>
From the Remarks section on the EventTrigger page on MSDN:
If you do choose to use Triggers, in Windows Runtime XAML, the default
behavior for event triggers and the only event that can be used to
invoke an EventTrigger is FrameworkElement.Loaded. Because that's both
the default and the only enabled behavior, don't set the RoutedEvent
attribute. Just use the XAML <EventTrigger>. For more info, see
Triggers.
I know it's too late for answering the question but for those who will be having the same issue.
you can use this XAML code:
<Image x:Name="TumbImage" Opacity="0" ...>
<i:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="ImageOpened">
<media:ControlStoryboardAction Storyboard="{StaticResource ImageOpacityStoryBoard}" ControlStoryboardOption="Play"/>
</core:EventTriggerBehavior>
</i:Interaction.Behaviors>
The Sample storyboard I used here:
<Storyboard x:Key="ImageOpacityStoryBoard">
<DoubleAnimation Storyboard.TargetName="TumbImage"
Storyboard.TargetProperty="Opacity"
From="0" To="1" Duration="0:0:0.33" />
</Storyboard>
and the XML namespaces used:
xmlns:i="using:Microsoft.Xaml.Interactivity"
xmlns:core="using:Microsoft.Xaml.Interactions.Core"
xmlns:media="using:Microsoft.Xaml.Interactions.Media"
I'm designing an UI atm and have the problem that the button animations don't fit the style of my Program and I would rather just change the colour.
My problem is: I just cant find a way do deactivate the animation that appears when you hover about or click a button :(
Is it even possible ?
Here is an image animation which is similar to an Button.
<Image Width="20" Height="20" Source="image\close.png"
ToolTip="close"
Opacity="0.5" Canvas.Left="720" Canvas.Top="3"
MouseLeftButtonDown="Close_MouseLeftButtonDown">
<Image.RenderTransform>
<RotateTransform x:Name="imgTransform"
CenterX="10"
CenterY="10"
Angle="0"/>
</Image.RenderTransform>
<Image.Triggers>
<EventTrigger RoutedEvent="Image.MouseEnter">
<BeginStoryboard HandoffBehavior="Compose" >
<Storyboard Name="myStoryBoard" >
<DoubleAnimation
Storyboard.TargetName="imgTransform"
Storyboard.TargetProperty="Angle"
By="90" Duration="0:0:.2"
/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Image>
image\close.png is my image and you can change into yours.
As you can test,it has a duration and will stop automatically.However you can get StoryBoard Object by its Name property in C# code and call Stop method on it.
I have a scrolling News Ticker in WPF that is used to convey alerts (their preference not mine). The implementation works but I have found an issue which is more noticeable as the text gets longer. The text scrolls and then abruptly stops and then restarts.
<Border Grid.Row="5" Grid.Column="0" Grid.ColumnSpan="5" BorderBrush="White" BorderThickness="0,3,0,3">
<StackPanel Orientation="Horizontal" x:Name="Ticker" Background="Transparent" >
<StackPanel.Triggers>
<EventTrigger RoutedEvent="FrameworkElement.Loaded">
<BeginStoryboard x:Name="NewsTicker">
<Storyboard RepeatBehavior="Forever">
<DoubleAnimation From="{Binding ElementName=Ticker, Path=ActualWidth}" To="{Binding ElementName=Ticker, Path=ActualWidth, Converter={StaticResource NegConverter}}" Storyboard.TargetName="translate" Storyboard.TargetProperty="X" Duration="0:0:35" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="WrapPanel.MouseEnter">
<PauseStoryboard BeginStoryboardName="NewsTicker" />
</EventTrigger>
<EventTrigger RoutedEvent="WrapPanel.MouseLeave">
<ResumeStoryboard BeginStoryboardName="NewsTicker" />
</EventTrigger>
</StackPanelPanel.Triggers>
<TextBlock Text="{Binding NewsTicker, IsAsync=True}" Margin="0,0,0,0" Padding="0,0,0,0" VerticalAlignment="Top" >
<TextBlock.RenderTransform>
<TranslateTransform x:Name="translate" />
</TextBlock.RenderTransform>
</TextBlock>
</StackPanelPanel>
</Border>
I would like the text to scroll from the Right to the Left until it scrolls off the screen and then come back in once again from the Right in an endless loop.
I tried a couple of different methods from SO, including animating Margins with Thickness animation and as well as an example using a canvas, but neither of them actually worked at all.
Any advice?
Okay, so the key to it not jumping is binding to the right things - we want to bind the To field to the ActualWidth of the TextBlock, not the ActualWidth of the StackPanel (which is smaller)
So we can change the DoubleAnimation to:
<DoubleAnimation From="{Binding ElementName=Ticker, Path=ActualWidth}" To="{Binding ElementName=TickerTextBlock, Path=ActualWidth, Converter={StaticResource NegConverter}}" Storyboard.TargetName="translate" Storyboard.TargetProperty="X" Duration="0:0:4" />
And add a x:Name="TickerTextBlock" to the TextBlock containing the Text.
Unfortunately, this doesn't work straight off (and instead it uses the initial value of 0 from the binding pre text loading for the animation! :|). Animations are often very fickle like this - I believe it has something to do with only being created and run once, and so the value of the binding is only used when the animation is first run. (but I may be mis-understanding)
Certainly one solution which worked on my copy of your code is to ensure that the text loads first, (ie we place the TextBlock above the animation in the XAML, and change the binding so that it is not async.)
If you wish to keep the async, then I believe we'd need to restart the double animation when the text changes. I would look into this further, but it's really late and I'm going to head to bed (sorry!)
I have a storyboard inside a data template. I want to start this only for a certain condition. In my case whenever the no. of seconds in the clock is 59 the storyboard should start.
Below you can see the storyboard as well as the control to which the animation is applied :
<!-- Minute Hand -->
<Image
Source="{Binding Time, Converter={StaticResource MinHandBackground}}"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Canvas.Left="118"
Canvas.Top="118">
<Image.RenderTransform>
<TransformGroup>
<TranslateTransform
X="-11"
Y="-90" />
<RotateTransform
x:Name="minHandTransform" />
</TransformGroup>
</Image.RenderTransform>
<Image.Triggers>
<EventTrigger
RoutedEvent="Image.Loaded">
<BeginStoryboard>
<Storyboard
x:Name="myStoryboard2">
<DoubleAnimation
x:Name="minuteAnimation"
Storyboard.TargetName="minHandTransform"
Storyboard.TargetProperty="Angle"
Duration="0:0:1"
From="{Binding Time, Converter={StaticResource minuteHandTransform}}"
To="{Binding Time, Converter={StaticResource minuteHandToTransform}}"
RepeatBehavior="1x">
<DoubleAnimation.EasingFunction>
<SineEase
EasingMode="EaseOut" />
</DoubleAnimation.EasingFunction>
</DoubleAnimation>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Image.Triggers>
</Image>
Currently the BeginStoryboard starts it immediately on load. I want to start it when the number of seconds is 59 but i cant access the storyboard in the back end as the control is within a data template.
Can anyone please help me out here.....i have been banging my head about this problem for a lot of days now!!
The easiest way would be to move the entire contents of the DataTemplate to a separate UserControl so you would get easier access to all named elements.