Strange EventTrigger "OnLoad" behavior WPF - c#

I'm simulating a car videogame scoreboard that is constantly updated and sorted. Everything works as I expected but then I tryied to add an EventTrigger that makes an animation when the new drivers are inserted to my list and that made strange behaviours.
<EventTrigger RoutedEvent="FrameworkElement.Loaded">
<!--...-->
</EventTrigger>
Sometimes when I fire that event in one car, it seems like more cars are suscripted to this event so that is not going as I expected. Obviously I would like every car had his own event. Moreover, that event is also fired when the scoreboard is sorted i.e when a car changes his position.
Now I'm going to explain what I have:
The following list is constantly updated (adds and update elements) and is not sorted by position because Im doing this directly on my view:
public ObservableCollection<Car> ListaObservable { get; set; }
The following code is extracted from the View:
<UserControl>
<UserControl.Resources>
<DataTemplate DataType="models:Car">
<!-- Each grid represents a car that should shine everytime it passes through the finish line -->
<Grid>
<!--...-->
<Grid.Triggers>
<!-- Everytime "Estimacion" is updated, that event is fired as I expected -->
<EventTrigger RoutedEvent="Binding.TargetUpdated">
<BeginStoryboard>
<Storyboard >
<!-- It doesn't matter what kind of animation is -->
<Duration="{Binding Estimacion, NotifyOnTargetUpdated=True}"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<!-- The event I mentioned before is never fired when I add a new car to my list of Cars so I created that event but sometimes it does strange behaviours -->
<EventTrigger RoutedEvent="FrameworkElement.Loaded">
<BeginStoryboard>
<!-- ... -->
</BeginStoryBoard>
</Eventrigger>
</Grid.Triggers>
</Grid>
</DataTemplate>
<CollectionViewSource x:Key="SortedItems" Source="{Binding ListaObservable}" IsLiveSortingRequested="True">
<CollectionViewSource.SortDescriptions>
<scm:SortDescription PropertyName="Posicion"/>
</CollectionViewSource.SortDescriptions>
</CollectionViewSource>
</UserControl.Resources>
<Grid>
<ListBox Name="ListBox" ItemsSource="{Binding Source={StaticResource SortedItems}}" BorderThickness="0" MinHeight="800" />
</Grid>
</UserControl>
Finally, In my case how can I fire an EventTrigger directly by my ViewModel?
How can I create a new event that can be fired everytime I want it?

Related

FlipView EventTrigger for the SelectionChanged event

I'm developing universal app. On one page i decided to use FlipView. I can easily animate SelectionChanged event from code-behind, but i'm just curious if there is a way to animate this event using XAML only. (BTW, UseTouchAnimationsForAllNavigation="True" doesnt work).
So, here's simplified example of what i'm doing :
<FlipView x:Name="MultipleItems">
<FlipView.Triggers>
<EventTrigger RoutedEvent="Selector.SelectionChanged">
<BeginStoryboard>
<Storyboard x:Name="ColorStoryboard">
//do stuff
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<FlipView.Triggers>
</FlipView>
I think this way of usage EventTrigger is fine (as far as SelectionChanged event takes arguments inherited from RoutedEventArgs), but it still gives me runtime error on navigation to page that contains FlipView.
Error is next :
WinRT information: Failed to assign to property 'Windows.UI.Xaml.EventTrigger.RoutedEvent'. [Line: 69 Position: 35]
Additional information: The text associated with this error code could not be found.
I believe there's way to assign that RoutedEvent property correctly, but i didnt find it yet. Also I don't wont to use behaviours for such simple thing.
Can anyone help?
You need to install the Microsoft.Xaml.Behaviors.Uwp.Managed in your project. Then the EventTrigger will be supported in an UWP project.
Then in your XAML use this package like this:
xmlns:Interactivity="using:Microsoft.Xaml.Interactivity"
xmlns:Core="using:Microsoft.Xaml.Interactions.Core"
xmlns:Media="using:Microsoft.Xaml.Interactions.Media"
Now you can for example change the background color of FlipView like this:
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid.Resources>
<Storyboard x:Key="std" x:Name="std" >
<ColorAnimation From="Red" To="Transparent" Duration="0:0:3"
Storyboard.TargetProperty="(Background).(SolidColorBrush.Color)"
Storyboard.TargetName="flipView"/>
</Storyboard>
</Grid.Resources>
<FlipView x:Name="flipView" ItemsSource="{x:Bind flipviewCollection}">
<Interactivity:Interaction.Behaviors>
<Core:EventTriggerBehavior EventName="SelectionChanged">
<Media:ControlStoryboardAction Storyboard="{StaticResource std}" />
</Core:EventTriggerBehavior>
</Interactivity:Interaction.Behaviors>
<FlipView.ItemTemplate>
<DataTemplate>
<Image Source="{Binding ImageSource}" Stretch="None"/>
</DataTemplate>
</FlipView.ItemTemplate>
</FlipView>
</Grid>
As you can see, I used EventTriggerBehavior and the event's name is SelectionChanged.

Rotating a visible set of elements in WPF

This is more for advertising deals to potential customers so there is no human interaction component to this.
Right now I just have the elements bound to an ItemsControl and a storyboard animation loops through. Unfortunately I want to show 4 items at a time, pause on them for 10 seconds, then show the next 4. I could have 5 coupons, I could have 30, so I can't enter anything in statically except that I know my visible width (they will be rotating horizontally) is 1920px.
My current implementation, which displays 1 to 4 then 5 to 8 and loops back to "1" is:
<ItemsControl Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="3" ItemsSource="{Binding Path=VisibleDigitalCoupons}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Left" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<DataTemplate.Resources>
<Storyboard x:Key="RotateDigitalCoupons" BeginTime="0:0:0" Duration="0:0:10" RepeatBehavior="Forever" Completed="RotateDigitalCouponsCompleted">
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(TranslateTransform.X)" Storyboard.TargetName="digitalCouponView">
<EasingDoubleKeyFrame KeyTime="0:0:5" Value="0"></EasingDoubleKeyFrame>
<EasingDoubleKeyFrame KeyTime="0:0:10" Value="-1920">
<EasingDoubleKeyFrame.EasingFunction>
<CubicEase EasingMode="EaseInOut"></CubicEase>
</EasingDoubleKeyFrame.EasingFunction>
</EasingDoubleKeyFrame>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</DataTemplate.Resources>
<views:DigitalCouponView x:Name="digitalCouponView" Margin="40,40,20,20" Height="240" Width="420" RenderTransformOrigin="0.5,0.5" >
<views:DigitalCouponView.RenderTransform>
<TransformGroup>
<TranslateTransform/>
</TransformGroup>
</views:DigitalCouponView.RenderTransform>
</views:DigitalCouponView>
<DataTemplate.Triggers>
<EventTrigger RoutedEvent="Window.Loaded">
<BeginStoryboard x:Name="RotateDigitalCoupons_BeginStoryboard" Storyboard="{StaticResource RotateDigitalCoupons}"/>
</EventTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Attempts to use the Completed event to fire off a refresh of the "visible coupons" and reuse the animation have met with failure because the animation is on repeatbehavior forever. However, even with that off, I don't get completed event firing, so that's a dead end AFAIK.
Anyone have any ideas or dealt with this before? Is my process flawed somehow?
Create a Dispatch Timer which contains a state machine which will execute the logic depending on the current state and handle the data driven components of what will be displayed. Within the timer turn on and off the animations as required.
You will need to make the animations more generic of course, but you have the framework which can be leveraged by the timer.

How to use condition in EventTrigger in WPF

I have two questions.
Q1: How can I use conditions in an EventTrigger?
In the following code you can see two EventTriggers for ListBoxItem. As you can see, This EventTriggers is for MouseEnter and MouseLeave event.
<UserControl.Resources>
<DataTemplate x:Key="DataTemplateItemDirect">
...
...
...
<DataTemplate.Triggers>
<EventTrigger SourceName="borderItem" RoutedEvent="Border.MouseLeave">
<BeginStoryboard>
<Storyboard>
<ParallelTimeline>
<ThicknessAnimationUsingKeyFrames Storyboard.TargetName="TitlePanel" Storyboard.TargetProperty="Margin" BeginTime="00:00:00">
<SplineThicknessKeyFrame KeyTime="00:00:00" Value="0,0,0,0" />
<SplineThicknessKeyFrame KeyTime="00:00:0.1" Value="0,40,0,0" />
</ThicknessAnimationUsingKeyFrames>
</ParallelTimeline>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger SourceName="borderItem" RoutedEvent="Border.MouseEnter">
<BeginStoryboard>
<Storyboard>
<ParallelTimeline>
<ThicknessAnimationUsingKeyFrames Storyboard.TargetName="TitlePanel" Storyboard.TargetProperty="Margin" BeginTime="00:00:00">
<SplineThicknessKeyFrame KeyTime="00:00:00" Value="0,40,0,0" />
<SplineThicknessKeyFrame KeyTime="00:00:0.1" Value="0,0,0,0" />
</ThicknessAnimationUsingKeyFrames>
</ParallelTimeline>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</UserControl.Resources>
Q2: In the server side code (C# Code), I've defined a public static bool variable and my condition is that when the variable was equal to True, EventTrigger do their work. My goal is to give the option to the users to enable or disable animations in EventTriggers.
So how can I do that?
Q1 : Conditions cannot be used in the XAML but in your code.
Q2 : Why is your bool variable declared as static ?
If you want to give the users a possibility to disable or enable something, may be you could use a checkbox and bind it to your bool variable.
In WPF, an EventTrigger in XAML simply hooks an event to an animation. Your requirements cannot be accomplished with XAML alone. One way that you could implement your requirements would be to attach an event handler to the events in XAML and then do the conditional part in the event handler.
First, you'll need to define your Storyboards in some Resources section so that you can access them from the event handler. Then you can check the value of your bool variable and programmatically start the Storyboard from there if it is true.
In Resources:
<Window.Resources>
<Storyboard x:Key="YourStoryboard" ... />
</Window.Resources>
In XAML:
<UserControl.Resources>
<DataTemplate x:Key="DataTemplateItemDirect">
...
<Border Name="borderItem" DataContext="{Binding}"
MouseEnter="MainWindow_MouseEnter" MouseLeave="MainWindow_MouseLeave" ... />
...
</DataTemplate>
</UserControl.Resources>
In the event handler:
private void MainWindow_MouseEnter(object sender, RoutedEventArgs e)
{
if ((sender.DataContext).YourBoolVariable)
{
Storyboard storyboard = (Storyboard)FindResource("YourStoryboard");
Storyboard.SetTarget(storyboard , YourControl);
storyboard.Begin();
}
}
UPDATE >>>
In order to deal with multiple animations individually, you can add the YourBoolVariable into your data item class (so that you have one for each animation). Then in the DataTemplate, you can set the DataContext of the Border to the data bound item and then retrieve it in the event handlers. The code example has been updated to reflect this.

Binding to ObservableCollection with transition

I have ItemsControl which I bind to ObservableCollection
On my view model I just insert object and it pops onto UI
I want to show transition. For example, I want this item to fade in so user registers this change visually, let's say it happens in 1 second.
What should I look for? How it's done in WPF?
EDIT:
I think I need some kind of animation but what I'm looking for is something simple without coding. Plain XAML implementation, is anything built-in? I tried TranslateTransform and other choices but it doesn't do anything.
<ItemsControl ItemsSource="{Binding Source={StaticResource TrucksSource}}">
<ItemsControl.RenderTransform>
<TranslateTransform />
</ItemsControl.RenderTransform>
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding TruckId}" Background="Aqua"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
For fade-in, you can use an EventTrigger on the Loaded event for the ContentPresenters
<ItemsControl ItemsSource="{Binding Source={StaticResource TrucksSource}}">
<ItemsControl.ItemContainerStyle>
<Style TargetType="ContentPresenter">
<Style.Triggers>
<EventTrigger RoutedEvent="Loaded">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard Storyboard.TargetProperty="Opacity">
<DoubleAnimation From="0.0"
To="1.0"
Duration="00:00:01"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
</Style.Triggers>
</Style>
</ItemsControl.ItemContainerStyle>
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding TruckId}" Background="Aqua"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Read this: http://msdn.microsoft.com/en-us/library/ms750596.aspx
You need Animating Transformations (last chapter) and change Opacity value from 1.0 to 0.0

animating an observablecollection move operation in the UI wpf?

I have a wrappanel bound to an observablecolelction.
Is there a way to animate the movement of items in the UI when the collection is changed in the code behind? Kind of like the fluid movement of windows tiles style metro apps?
Any design ideas of how to go about this will be appreciated.
Right now, all I can think of is animating the layout chaging event?
Thanks
I've needed such thing in the past and -as I remember- I ended using a slightly modified version of the sample provided here:
https://learn.microsoft.com/en-us/archive/blogs/devdave/layout-transitions-an-animatable-wrappanel
This sample is somewhat advanced and supports animating the items when any modification is made to the collection (adding items, deleting items, resizing the panel)
On the other hand if what you need is a simple animation at the item level only (e.g. when an item is appearing/disappearing) it's much simpler you can build an ItemTemplate with a control that has an EventTrigger for the relevant event. This example will animate the item when added:
XAML:
<StackPanel>
<ItemsControl x:Name="itemsControl" Height="300">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel IsItemsHost="True" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Rectangle Width="80" Height="80" Fill="Red" Margin="4" Opacity="0">
<Rectangle.RenderTransform>
<TranslateTransform Y="20" />
</Rectangle.RenderTransform>
<Rectangle.Triggers>
<EventTrigger RoutedEvent="Loaded">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Opacity" To="1" Duration="00:00:00.6" />
<DoubleAnimation Storyboard.TargetProperty="(RenderTransform).Y" To="0" Duration="00:00:00.4" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Rectangle.Triggers>
</Rectangle>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<Button Width="60" Height="40" Content="Add Item" Click="Button_Click" />
</StackPanel>
Code behind:
ObservableCollection<string> items = new ObservableCollection<string>();
public MainWindow()
{
InitializeComponent();
itemsControl.ItemsSource = items;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
items.Add("New Item");
}

Categories