WPF how to animate picture inside listboxitem using data trigger? - c#

I have a listbox with templated listboxitem which is binded to an object.
one of the listboxitem controls is an image and it's source is also binded to a property inside the datacontext, So the pictur inside the Image control is different according to that property.
I want to animate the image but only specific one (when the binded property value is some specific value only).
I guess there are other ways to do that, maybe using code behind. But I would realy like use it in the xaml code, so I thought using animation inside DataTrigger, it makes sense to me because the starting/ending the animation depends on the DataContext:
<DataTrigger Binding="{Binding Path=Value.SomeProperty}"
Value="SomePropertyValue">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard TargetName="SomePropertyIcon">
<StaticResource ResourceKey="SomePropertyValueAnimation"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<BeginStoryboard>
<Storyboard TargetName="SomePropertyIcon">
<StaticResource ResourceKey="StopSomePropertyValueAnimation"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.ExitActions>
</DataTrigger>
But I cannot make it work any way.
I tried to put that code inside a style of the listBox and then apply that style to the image, it didnt work, I also tried to put inside the listBox.ItemTamplate, and other places but nothing helped.
Here is the code. maybe it can help to understand (I pasted only relevan code here):
<UserControl ...>
<UserControl.Resources>
<local:SomePropertyToImageConverter x:Key="somePropertyToImageConverter"/>
<DoubleAnimation x:Key="SomePropertyValueAnimation" Storyboard.TargetProperty="(UIElement.RenderTransform).(RotateTransform.Angle)" To="45" Duration="0:0:2" RepeatBehavior="Forever"/>
<DoubleAnimation x:Key="StopSomePropertyValueAnimation" Storyboard.TargetProperty="(UIElement.RenderTransform).(RotateTransform.Angle)" To="0" Duration="0:0:2"/>
</UserControl.Resources>
<Grid>
<ListBox Name="myListBox"
ItemsSource="{Binding Path=myDataContext}" IsEnabled="True" Focusable="True" SelectionMode="Extended">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Image Width="20" Height="20" Name="SomePropertyIcon"
Source="{Binding Path=Value.SomeProperty, Converter={StaticResource somePropertyToImageConverter}}"/>
</Grid>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding Path=Value.SomeProperty}"
Value="SomePropertyValue">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard TargetName="SomePropertyIcon">
<StaticResource ResourceKey="SomePropertyValueAnimation"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<BeginStoryboard>
<Storyboard TargetName="SomePropertyIcon">
<StaticResource ResourceKey="StopSomePropertyValueAnimation"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.ExitActions>
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</ListBox.ItemTemplate>
<ListBox.ItemContainerStyle>
<Style TargetType="{x:Type ListBoxItem}">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=Value.SomeProperty}"
Value="SomePropertyValue">
<Setter Property="Background" Value="Bisque"/>
</DataTrigger>
</Style.Triggers>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
</Grid>

Animation cannot change property values for a property path that does not exist. The "Image" you are trying to manipulate does not have "Rotate transform" you should add it and it should work:
<Image Width="20" Height="20" Name="SomePropertyIcon"
Source="{Binding Path=Value.SomeProperty, Converter={StaticResource somePropertyToImageConverter}}">
<Image.RenderTransform >
<RotateTransform />
</Image.RenderTransform>
</Image>

Related

Binding to an ancestor property inside an animation in WPF

I have a WPF page with a TabControl in it, and I wanted to design the tabs to have a nice underline when selected and on mouse hover.
I've managed to do that but each tab has a different length, and it seems that I can't bind a property of the TabItem inside of its animation.
This is what i have done so far:
<TabControl Width="{Binding ActualWidth,
ElementName=cnvsMain}" Height="{Binding
ActualHeight, ElementName=cnvsMain}"
BorderThickness="0" >
<TabControl.Resources>
<Style TargetType="TabItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="TabItem">
<StackPanel>
<Border Name="Border"
BorderThickness="1,1,1,0"
BorderBrush="Gainsboro"
CornerRadius="6,6,0,0"
Margin="2,0">
<ContentPresenter x:Name="ContentSite"
VerticalAlignment="Center"
HorizontalAlignment="Center"
ContentSource="Header"
Margin="10,2"/>
</Border>
<Label Name="TabUnderline" Background="LimeGreen"
Margin=" 5, 0" Height="5" Width="0"/>
</StackPanel>
<ControlTemplate.Triggers>
<Trigger Property="IsSelected" Value="True">
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Storyboard.TargetName="TabUnderline"
Storyboard.TargetProperty="Width"
From="0" To="100"
Duration="0:0:0.1"
AutoReverse="False" />
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
<Trigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Storyboard.TargetName="TabUnderline"
Storyboard.TargetProperty="Width"
From="100" To="0" Duration="0:0:0.1"
AutoReverse="False" />
</Storyboard>
</BeginStoryboard>
</Trigger.ExitActions>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</TabControl.Resources>
<TabItem Header="Rooms">
<StackPanel>
<Label Content="TODO: List all rooms" />
<Label Content="TODO: Create room button" />
</StackPanel>
</TabItem>
<TabItem Header="Statistics" />
<TabItem Header="Details" />
</TabControl>
It works great but there is one simple flaw:
The under line will always be 100 pixels (Or is it units?) wide, and each tab has its unique and different width. I've tried to replace the hard-coded 100 to {Binding ActualWidth} or using a RelativeSource to get the width of the TabItem, but I keep getting an exception everytime this page is being loaded because of a binding-error.
How can I bind a TabItem's property to a value inside its own animation? Is it even possible?

Toggle opacity of control

I have button, which should toggle Opacity of element. Currently it works only at half - on click make control visible( set opacity to 1). I've tried use ToogleButton instead of simple Button to use properties IsChechecked (I think it is the same as IsClicked) but what I found wasn't good for me, cause for this properties I can't set TargetName for animation.
<ToggleButton BorderThickness="0" Background="Transparent" Grid.Column="0" Padding="0" Grid.Row="0" Focusable="False" Width="Auto" PreviewMouseUp="Share_Click">
<ToggleButton.Content>
<Grid HorizontalAlignment="Stretch">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<TextBlock Margin="0,0,6,0" Grid.Column="0" Text="Filter" Foreground="#FFC0B6D1" FontFamily="Segoe UI Semibold" FontSize="14" />
<Image Grid.Column="1" Margin="0,4,0,0" VerticalAlignment="Center" Height="6" Width="12" Name="FiltersToggleArrow" Style="{StaticResource ToogleFilters}"/>
</Grid>
</ToggleButton.Content>
<ToggleButton.Triggers>
<EventTrigger RoutedEvent="UIElement.PreviewMouseDown">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation BeginTime="0:0:0.00" Storyboard.TargetName="FiltersPanel" Storyboard.TargetProperty="Opacity" From="0" To="1" Duration="0:0:00.2" AutoReverse="False"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</ToggleButton.Triggers>
</ToggleButton>
The MSDN documentation says:
You can set this property to the name of any element within the scope of where the setter collection (the collection that this setter is part of) is applied. This is typically a named element that is within the template that contains this setter.
To get around this, you can instead apply your Storyboard inside the Style of your Panel, not the ToggleButton. In this case, you can use a DataTrigger to bind to the IsChecked property and act accordingly.
Here's an example I did with a ToggleButton and a Rectangle.
<ToggleButton Content="Switch" Name="chk"/>
<Rectangle Fill="Red" Name="rect"
Width="50" Height="50">
<Rectangle.Style>
<Style TargetType="Rectangle">
<Style.Triggers>
<DataTrigger Binding="{Binding IsChecked, ElementName=chk}" Value="False">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard
Storyboard.TargetProperty="Opacity">
<DoubleAnimation From="1"
To="0"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<BeginStoryboard>
<Storyboard
Storyboard.TargetProperty="Opacity">
<DoubleAnimation From="0"
To="1"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.ExitActions>
</DataTrigger>
</Style.Triggers>
</Style>
</Rectangle.Style>
</Rectangle>
From my experience with triggers, they can be a royal pain in the backside and are tricky to get right. That being said, this example should be enough to fit your requirements.

How to make a TextBox and a ComboBox's ToggleBox to flicker identically, when certain dependency property is set to True?

I have a TextBox and a ComboBox. I want them both to flicker whenever some dependency property, BlinkDepProp, is set to true:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<!--
...
-->
<TextBox Text="{Binding MyDataContext.SomeText, UpdateSourceTrigger=PropertyChanged}" Grid.Row="0">
<TextBox.Style>
<Style>
<Style.Triggers>
<DataTrigger
Binding="{Binding Path=BlinkDepProp,
RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type loc:DevicesRepositoryEditorUserControl}}}"
Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard Name="BlinkBeginStoryboard">
<Storyboard>
<ColorAnimation To="Red" Storyboard.TargetProperty="(TextBox.Background).(SolidColorBrush.Color)"
FillBehavior="Stop" Duration="0:0:0.4" RepeatBehavior="Forever" AutoReverse="True" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<StopStoryboard BeginStoryboardName="BlinkBeginStoryboard" />
</DataTrigger.ExitActions>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
<!--
...
-->
<ComboBox ItemsSource="{Binding MyDataContext.SomeList}" Template="{DynamicResource ComboBoxControlTemplate}"
SelectedValue="{Binding MyDataContext.SelectedEntry}"
Grid.Row="1">
<!--
...
-->
</Grid>
The snippet above works for the TextBox element, but I want to apply it to the ToggleButton of the ComboBox as well. I created the ComboBoxControlTemplate ControlTemplate (doesn't really matter, but it's in some ResourceDictionary that resides in a separate XAML file).
My question is: how do I trigger both the TextBox and the ComboBox to act identically (same colours, identical flickering rate and phase, ...), and respond to the same BlinkDepProp (defined in the containing UserControl)?
What exactly didn't work?
On my little example textbox and combobox are blinking.
<Window x:Class="animation.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<Style x:Key="blink">
<Style.Triggers>
<DataTrigger
Binding="{Binding BlinkDepProp}"
Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard Name="BlinkBeginStoryboard">
<Storyboard>
<ColorAnimation To="Red" Storyboard.TargetProperty="(Background).(SolidColorBrush.Color)"
FillBehavior="Stop" Duration="0:0:0.4" RepeatBehavior="Forever" AutoReverse="True" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<StopStoryboard BeginStoryboardName="BlinkBeginStoryboard" />
</DataTrigger.ExitActions>
</DataTrigger>
</Style.Triggers>
</Style>
</Window.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<TextBox
Name="txt"
Text="{Binding SomeText, UpdateSourceTrigger=PropertyChanged}"
Style="{StaticResource ResourceKey=blink}"
Background="Beige"
Width="100"
Height="20"/>
<ComboBox
Name="cmb"
Style="{StaticResource ResourceKey=blink}"
Background="Beige"
Width="100"
Height="20"
Grid.Column="1"/>
</Grid>
</Window>
You can try setting the ComboBox's Background to some Binding to the TextBox's Background when the DataTrigger is fired like this:
<ComboBox ItemsSource="{Binding MyDataContext.SomeList}"
Template="{DynamicResource ComboBoxControlTemplate}"
SelectedValue="{Binding MyDataContext.SelectedEntry}"
Grid.Row="1">
<ComboBox.Style>
<Style TargetType="ComboBox">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=BlinkDepProp,
RelativeSource={RelativeSource
AncestorType={x:Type loc:DevicesRepositoryEditorUserControl}}}" Value="True">
<Setter Property="Background" Value="{Binding Background, ElementName=tb}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</ComboBox.Style>
</ComboBox>
I suppose your TextBox's Name is tb.

Wpf button with both result datatrigger storyboard and mouseover background

I'm looking to implement this functionality: A button, bound to a command, that can signal back wether the command was successful in form of a red or green flash. At the same time, I want the button to look like other buttons.
This is the code I have now:
<Button IsEnabled="{Binding EmptyingAllowed}" Content="Töm lista" Foreground="#555555" FontWeight="SemiBold" Height="20" Width="65" Command="{Binding EmptyListCommand}" >
<Button.Style>
<Style TargetType="Button">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border BorderBrush="#BBBBBB" Padding="1" BorderThickness="0,0,1,1" Background="#DDDDDD">
<Grid Background="{TemplateBinding Background}">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" />
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<DataTrigger Binding="{Binding ClearedOk}" Value="Ok">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<ColorAnimation AccelerationRatio="0.2" DecelerationRatio="0.01" AutoReverse="True" Duration="0:0:1" To="LawnGreen" Storyboard.TargetProperty="(Button.Background).(SolidColorBrush.Color)"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
</DataTrigger>
<DataTrigger Binding="{Binding ClearedOk}" Value="Error">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<ColorAnimation AccelerationRatio="0.2" DecelerationRatio="0.01" AutoReverse="True" Duration="0:0:1" To="Red" Storyboard.TargetProperty="(Button.Background).(SolidColorBrush.Color)"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
When the command is executed, ClearedOk is set to "Ok" or "Error" and then to "Empty", this causes the flash I'm looking for, unfortunately, the usual mouseOver-effect is lost for some reason, and if I add a mouseOver-trigger, it always takes precedence over the flashing effect, meaning the flash is only visible of the button isn't mouseOver:ed.
Is there a solution to this?
Try to make datatemplate and model with properties of state and then binding in template to this properties.
<DataTemplate x:Key="DataTemplate">
<Grid >
<Ellipse Stroke="White"
StrokeThickness="5"
Fill="{Binding State, Converter={StaticResource CheckToColorConverter}}"/>
</Grid>
</DataTemplate>

Basic Animation in WPF: How to animate a grid when expanding

I am using MVVm in my WPF application. I am currently playing around with animating a grid so that when a user selected a particular item in a combobox I want my grid to animate as it expand. I have a property in my View Model that I want to bind to trigger the animation in my grid. How do we do this in MVVM? So far I have recently found a solution using
EventTrigger RoutedEvent ="SampleGridEventName"
is there any way to use other triggers to bind to a property in my ViewModel?Something like
Trigger BlahBlah ="{Binding ExpandGrid}"
Yes. Style has DataTrigger which also has EnterActions property. Animations are stored in resources. That's the trick.
<UserControl x:Class="TriggerSpike.UserControl1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Height="300" Width="300">
<UserControl.Resources>
<DoubleAnimation x:Key="SearchAnimation" Storyboard.TargetProperty="Opacity" To="1" Duration="0:0:4"/>
<DoubleAnimation x:Key="StopSearchAnimation" Storyboard.TargetProperty="Opacity" To="0" Duration="0:0:4"/>
</UserControl.Resources>
<StackPanel>
<TextBlock Name="progressWheel" TextAlignment="Center" Opacity="0">
<TextBlock.Style>
<Style>
<Style.Triggers>
<DataTrigger Binding="{Binding IsBusy}" Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<StaticResource ResourceKey="SearchAnimation"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<StaticResource ResourceKey="StopSearchAnimation"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.ExitActions>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
Searching
</TextBlock>
<Label Content="Here your search query"/>
<TextBox Text="{Binding SearchClause}"/>
<Button Click="Button_Click">Search!</Button>
<TextBlock Text="{Binding Result}"/>
</StackPanel>
</UserControl>

Categories

Resources