EventTriggers on an Image - c#

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"

Related

Invoking animation in a different xaml control on button click WITHOUT resorting to ViewModels

I have the following xaml:
<Button Content="{Binding Header}" Background="Blue" Name="PanelHeader">
<Button.Triggers>
<EventTrigger RoutedEvent="Button.Click">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetName="Stackpanel"
Storyboard.TargetProperty="Height"
From="0" To="100" Duration="0:0:3" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Button.Triggers>
</Button>
I want to move the storyboard animation to the stack panel declaration and simply invoke that animation from the button click.
Most examples I've found resort to using the viewmodel or other codebehind interjections.
this example doesn't use code behind nor ViewModel
http://www.java2s.com/Tutorial/VB/0290__Windows-Presentation-Foundation/StartandstopanAnimationwithButtonactions.htm

EventTrigger trigger on condition

I have a event trigger.
I want it to be enabled only if condition occures
for example only if Viewmodel.IsEnabled propery is true AND EventTrigger RoutedEvent="Window.Loaded" occured
my problem is that MultiDataTrigger and MultiTriggers cannot combine Event trigger with Data Trigger.
<DataTemplate.Triggers>
<EventTrigger RoutedEvent="Window.Loaded" SourceName="NotificationWindow">
<BeginStoryboard x:Name="FadeInStoryBoard">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="NotificationWindow" From="0.01" To="1" Storyboard.TargetProperty="Opacity" Duration="0:0:2"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</DataTemplate.Triggers>
In other words I have a trigger to load a story board when window is loaded.
I want to be able to enable/disable this trigger per item.
You can use Blend Interactivity for WPF for accomplishing you task. I do not know your whole DataTemplate, so in my sample I will use an invented one.
Let's suppose I have a collection of Person objects and I want to start the DoubleAnimation just per Person whose property IsEnabled is true. I bind my collection to an ItemsControl and I create a "conditional" DataTemplate:
<ItemsControl ItemsSource="{Binding Path=People}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border Name="Border" BorderBrush="Gray" BorderThickness="1" CornerRadius="4" Margin="2">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Loaded">
<i:Interaction.Behaviors>
<ei:ConditionBehavior>
<ei:ConditionalExpression>
<ei:ComparisonCondition LeftOperand="{Binding IsEnabled}" RightOperand="True"/>
</ei:ConditionalExpression>
</ei:ConditionBehavior>
</i:Interaction.Behaviors>
<ei:ControlStoryboardAction ControlStoryboardOption="Play">
<ei:ControlStoryboardAction.Storyboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetName="Border" From="0.01" To="1" Storyboard.TargetProperty="Opacity" Duration="0:0:2"/>
</Storyboard>
</ei:ControlStoryboardAction.Storyboard>
</ei:ControlStoryboardAction>
</i:EventTrigger>
</i:Interaction.Triggers>
<TextBlock Text="{Binding Path=Surname, Mode=OneWay}" Margin="2" />
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Of course you have to declare those namespaces in your XAML:
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"
The ConditionBehavior object evaluates the ComparisonCondition: if the former is true, it allows the ControlStoryboardAction to run.
I hope this small sample can give you an hint for solving your issue.

Fading animation in Visual Studio Blend

I wish to change the actual content of my textblock. I know I can change it by using textblock.text = "new text" when an event is triggered. However, is there a way to make it change gradually, i.e. with a fade animation? I tried using Visual Studio Blend, and managed to make objects scale, and rotate, but I have not figured out how to make content of an element change/ apply animation when content of the element changes.
Here is a small example, which consists of a TextBox and a TextBlock. Each time you change the value in the TextBox, the content of the TextBlock fades out and in.
<StackPanel>
<TextBox x:Name="txtSampleText" Margin="10" Text="This is a Sample Text"></TextBox>
<TextBlock x:Name="tbMessage" Text="{Binding ElementName=txtSampleText, Path=Text, NotifyOnTargetUpdated=True}" Margin="10">
<TextBlock.Triggers>
<EventTrigger RoutedEvent="Binding.TargetUpdated">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Opacity" Duration="0:0:1" From="1.0" To="0.0" />
<DoubleAnimation Storyboard.TargetProperty="Opacity" Duration="0:0:1" From="0.0" To="1.0" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</TextBlock.Triggers>
</TextBlock>
</StackPanel>

WPF Event Trigger Change Other UI element

I defined ListBox in my XAML which uses ItemTemplate.
Inside the ItemTemplate I placed Image.
<ListBox.ItemTemplate>
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel x:Name="itmTempPanel" IsItemsHost="True" ItemWidth="60" ItemHeight="60" Width="{Binding ElementName=lstFilesDropped, Path=Width}"/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
...
<Image>
<Image.Triggers>
<EventTrigger RoutedEvent="MouseEnter">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Height" To="71" Duration="0:0:0.3" />
<DoubleAnimation Storyboard.TargetName="itmTempPanel" Storyboard.TargetProperty="Height" To="71" Duration="0:0:0.3" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Image.Triggers>
</Image>
</ListBox.ItemTemplate>
When the mouse enter the image I want to begin storyboard on that image height and on the WrapPanel which I defined inside the ItemsPanelTemplate.
When the mouse enter to this image I got the following exception:
"'itmTempPanel' name cannot be found in the name scope of 'System.Windows.Controls.Image'."
How can I change other element property from element that begin the storyboard.
Thank you for your help !!
There are 2 ways to solve this. The first is using {x:Reference} a feature in .NET 4.0 for WPF. You should follow this approach if your application targets .NET 4.0. The idea is setting the Storyboard.Target to the object you want to animate (in this case is the WrapPanel). Although we can use Binding for Storyboard.Target but we cannot use RelativeSource or ElementName to set the binding source because Storyboard (or Timeline) is not a FrameworkElement (or FrameworkContentElement). The only way to specify the source is setting the Source property. However we can normally set this to a StaticResource or DynamicResource or directly (using element syntax). It's fortunate that the {x:Reference} was introduced in .NET 4.0 and this can help you reference any named object inside XAML (the way it works is not like ElementName). Here is the code for the first approach:
<DoubleAnimation Storyboard.Target="{Binding Source={x:Reference itmTempPanel}}"
Storyboard.TargetProperty="Height"
To="71" Duration="0:0:0.3" />
The second approach is based on DataTrigger. However this trigger is not for Image, it's exactly for the WrapPanel. But now the ElementName can be used to bind the Trigger source to the Image. So this approach is usable when {x:Reference} is not supported.
<WrapPanel x:Name="itmTempPanel" IsItemsHost="True" ItemWidth="60" ItemHeight="60"
Width="{Binding ElementName=lstFilesDropped, Path=Width}">
<WrapPanel.Style>
<Style TargetType="WrapPanel">
<Style.Triggers>
<DataTrigger Binding="{Binding IsMouseOver,ElementName=image}"
Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Height"
To="71" Duration="0:0:0.3" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
</DataTrigger>
</Style.Triggers>
</Style>
</WrapPanel.Style>
</WrapPanel>
<Image Name="image">
<Image.Triggers>
<EventTrigger RoutedEvent="MouseEnter">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Height" To="71"
Duration="0:0:0.3" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Image.Triggers>
</Image>
Note that you have to give the Image a name (such as image). The <DoubleAnimation> for the WrapPanel is removed. Instead of using EventTrigger, we used DataTrigger listening to IsMouseOver property. You can also specify the DataTrigger.ExitActions to start animating when IsMouseOver is false (equal to MouseLeave).

How to start a storyboard which is attached to a control inside a data template?

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.

Categories

Resources