Binding from DataTemplate.Triggers - c#

I am trying to do my binding working for MyAnimation similar to the working one which is MyHeight.
My Height is working properly, but I can not find a way to make the MyAnimation binds to something.
<DataTemplate
x:Key="MyTemplate"
DataType="MyNotification">
<Grid
x:Name="MyNotificationWindow" ...
Height="{Binding Path = MyHeight}"
>
....
</Grid>
<DataTemplate.Triggers>
<EventTrigger
RoutedEvent="Window.Loaded"
SourceName="MyNotificationWindow">
<BeginStoryboard
x:Name="MyStory">
<Storyboard>
<DoubleAnimation
Storyboard.TargetName="MyNotificationWindow"
From="0.01"
To="1"
Storyboard.TargetProperty="Opacity"
Duration="{Binding Path=MyAnimation, RelativeSource={RelativeSource TemplatedParent}}"
/>
....
</DataTemplate.Triggers>
Thank you!

I think the issue is the following : {Binding Path=MyAnimation, RelativeSource={RelativeSource TemplatedParent}} should be {Binding Path=DataContext.MyAnimation, RelativeSource={RelativeSource Self}}
Try this, it might be the issue.

Related

EventTriggers on an Image

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"

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.

WPF Expander to expand on far right side of application

I would like to alter my program so that when an image is clicked it reveals a sidebar on the far right side of the application.
I have currently been trying to achieve this using an Expander. This is what it looks like so far:
<Expander>
<Expander.Header>
<Image Width="200" Height="300" Source="{Binding image}"/>
</Expander.Header>
<i:Interaction.Behaviors>
<local:UniqueNameBehavior ID="{Binding id}"/>
</i:Interaction.Behaviors>
<StackPanel Margin="10,4,0,0">
<ToggleButton Margin="4" Content="Option 1" Template="{StaticResource SimpleExpanderButtonTemp}"/>
<ListView>
<TextBox Text="Search" />
</ListView>
</StackPanel>
</Expander>
The idea is that whenever any movie is clicked, a sidebar on the far right side of the app is revealed containing info about the movie. If another movie is subsequently clicked, the sidebar will now contain info about the new movie.
I know that it is possible to change the ExpandDirection, but this only results in the content appearing beside the image as opposed to the far right of the application.
I've also tried adjusting the Margin of StackPanel but this results in the surrounding movies being pushed aside.
Could someone please help me out with how I can achieve this?
Thank you for your help!
I don't believe this kind of behaviour can be achieved using expanders the way you are using.
Why don't you create a grid on the far right side of your app with its width set to 0 and when you click/select a movie it sets the grid width to a new value and change its content based on the movie?
<Grid HorizontalAlignment="Right">
<Grid.Style>
<Style TargetType="Grid">
<Setter Property="Width" Value="0"/>
<Style.Triggers>
<DataTrigger Binding="{Binding ShowMovieForm}" Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Duration="0:0:0.1" Storyboard.TargetProperty="Width" To="400" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Duration="0:0:0.1" Storyboard.TargetProperty="Width" To="0" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.ExitActions>
</DataTrigger>
</Style.Triggers>
</Style>
</Grid.Style>
</Grid>

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).

Storyboard Completed Event from Style?

I'm new to Storyboard animations but I think this might be a problem I can't workaround the easy way. Nevertheless I try my luck here, maybe some of you guys know how to help me.
My Scenario : I want to show a popup in my Application that has a fadein effect. I also want to do this via MVVM so my control that wraps the fadein effect and the popup should no use codebehind and my application should just need to reset the datacontext of this control to a new viewmodel to show a new message.
My Problem is that I cannot determine when the animation is finished because I need to set the fadein Animation in the style.
My XAML looks like this :
<UserControl.Resources>
<Style x:Key="popupStyle" TargetType="{x:Type Border}" >
<Style.Triggers>
<Trigger Property="Visibility" Value="Visible">
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard x:Name="FadingStoryBoard">
<DoubleAnimation Storyboard.TargetProperty="Opacity" From="0.05" To="1" BeginTime="0:0:1" Duration="0:0:2.5" >
<DoubleAnimation.EasingFunction>
<ExponentialEase Exponent="5" EasingMode="EaseIn" />
</DoubleAnimation.EasingFunction>
</DoubleAnimation>
<DoubleAnimation Storyboard.TargetProperty="Opacity" From="1" To="0" BeginTime="0:0:6" Duration="0:0:8.5" >
<DoubleAnimation.EasingFunction>
<ExponentialEase Exponent="15" EasingMode="EaseOut" />
</DoubleAnimation.EasingFunction>
</DoubleAnimation>
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
</Trigger>
</Style.Triggers>
</Style>
</UserControl.Resources>
<Popup Name="Popup" IsOpen="{Binding IsVisible}" Height="{Binding PopupHeight}" Width="{Binding PopupWidth}" VerticalOffset="{Binding PopupVerticalOffset}" HorizontalOffset="{Binding PopupHorizontalOffset}" PopupAnimation="Fade" AllowsTransparency="True">
<Border Style="{StaticResource popupStyle}" Name="PopupContent" Padding="1" BorderBrush="#000000" Background="AliceBlue" CornerRadius="5" BorderThickness="3,3,3,3">
<!-- Events -->
<interact:Interaction.Triggers>
<interact:EventTrigger EventName="PreviewMouseDown">
<cmd:EventToCommand Command="{Binding Path=PopupMouseDownCommand}" PassEventArgsToCommand="True" />
</interact:EventTrigger>
</interact:Interaction.Triggers>
<DockPanel Name="ContentContainer" Background="Black" LastChildFill="True">
<Image Source="{Binding MessageIcon}" DockPanel.Dock="Left" Margin="5,0,5,0" Width="32" Height="32" />
<StackPanel Background="Transparent" DockPanel.Dock="Right" Margin="3">
<TextBlock Name="PopupHeaderTextBlock" Margin="0,3,0,5" TextWrapping="Wrap" FontSize="10" Text="{Binding PopupHeaderText}" Foreground="White" Background="Transparent" />
<TextBlock Name="PopupTextBlock" Text="{Binding PopupText}" TextWrapping="Wrap" FontSize="10" Foreground="White" Background="Transparent" />
</StackPanel>
</DockPanel>
</Border>
</Popup>
Anyone any ideas how I can get a notification in my ViewModel when the Storyboard has finished ?
You can handle the Completed event on the storyboard.
Documentation Here: http://msdn.microsoft.com/en-us/library/system.windows.media.animation.timeline.completed.aspx
Here's the code to attach the event from the codebehind:
call from the constructor:
private void AttachToCompletedEvent()
{
Style popupStyle = Resources["popupStyle"];
TriggerBase trigger = popupStyle.Triggers[0];
BeginStoryboard action = trigger.EnterActions[0] as BeginStoryboard;
Storyboard storyboard = action.Storyboard;
storyboard.Completed += CompletedEventHandler;
}
I think that should work for the code you provided.

Categories

Resources