Animate from one style to another - wpf

Just wondering if anyone know how to animate from one style to another i.e. going from NormalStyle to ActiveStyle when the user focuses on the textbox
<Style x:key="NormalStyle" TargetType="{x:Type TextBox}">
<Setter Property="BorderBrush" Value="Gray" />
<Setter Property="BorderThickness" Value="2" />
</Style>
<Style x:key="ActiveStyle" TargetType="{x:Type TextBox}" BasedOn="{StaticResource NormalStyle}">
<Setter Property="BorderBrush" Value="Green" />
<Setter Property="BorderThickness" Value="4" />
</Style>

If you want to swap the whole style, I think you might have to do it in code:
<StackPanel>
<TextBox Style="{StaticResource NormalStyle}" GotFocus="TextBox_GotFocus" LostFocus="TextBox_LostFocus"/>
<TextBox Style="{StaticResource NormalStyle}" GotFocus="TextBox_GotFocus" LostFocus="TextBox_LostFocus"/>
</StackPanel>
private Style focusedStyle;
private Style normalStyle;
public MainWindow()
{
InitializeComponent();
focusedStyle = FindResource("ActiveStyle") as Style;
normalStyle = FindResource("NormalStyle") as Style;
}
private void TextBox_GotFocus(object sender, RoutedEventArgs e)
{
((TextBox)sender).Style = focusedStyle;
}
private void TextBox_LostFocus(object sender, RoutedEventArgs e)
{
((TextBox)sender).Style = normalStyle;
}
Otherwise, you are pretty much confined to triggering back and forth..
<TextBox Text="1" >
<TextBox.Style>
<Style BasedOn="{StaticResource NormalStyle}" TargetType="{x:Type TextBox}">
<Style.Triggers>
<EventTrigger RoutedEvent="GotFocus">
<BeginStoryboard>
<Storyboard>
<ColorAnimation Storyboard.TargetProperty="BorderBrush.Color" To="Green" Duration="0:0:0.1" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="LostFocus">
<BeginStoryboard>
<Storyboard>
<ColorAnimation Storyboard.TargetProperty="BorderBrush.Color" To="Gray" Duration="0:0:0.1" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Style.Triggers>
</Style>
</TextBox.Style

Related

Context menuitems are not firing Click events

I tried to open the ContextMenu on button left click not on the right clcik so I did something as below. But menu items 'All' and 'Selected' are not firing their respective events in .cs.
Xaml
<Button Content="Import" ContextMenuService.IsEnabled="false">
<Button.Style>
<Style TargetType="{x:Type Button}">
<Style.Triggers>
<EventTrigger RoutedEvent="Click">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard>
<BooleanAnimationUsingKeyFrames Storyboard.TargetProperty="ContextMenu.IsOpen">
<DiscreteBooleanKeyFrame KeyTime="0:0:0" Value="True"/>
</BooleanAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
</Style.Triggers>
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu>
<MenuItem Header="All"
Click="ImportAll_Click"/>
<MenuItem Header="Selected"
Click="ImportSelected_Click"/>
</ContextMenu>
</Setter.Value>
</Setter>
</Style>
</Button.Style>
</Button>
Xaml.cs
private void ImportAll_Click(object sender, RoutedEventArgs e)
{
}
private void ImportSelected_Click(object sender, RoutedEventArgs e)
{
}
Is there something that I am missing here?
Try to define the ContextMenu as a resource:
<Button Content="Import" ContextMenuService.IsEnabled="false">
<Button.Resources>
<ContextMenu x:Key="cm">
<MenuItem Header="All" Click="ImportAll_Click"/>
<MenuItem Header="Selected" Click="ImportSelected_Click"/>
</ContextMenu>
</Button.Resources>
<Button.Style>
<Style TargetType="{x:Type Button}">
<Style.Triggers>
<EventTrigger RoutedEvent="Click">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard>
<BooleanAnimationUsingKeyFrames Storyboard.TargetProperty="ContextMenu.IsOpen">
<DiscreteBooleanKeyFrame KeyTime="0:0:0" Value="True"/>
</BooleanAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
</Style.Triggers>
<Setter Property="ContextMenu" Value="{StaticResource cm}" />
</Style>
</Button.Style>
</Button>

WPF: Hiding object causes brief flash before disappearing

I have a simple button style which has data trigger which binds to a bool property and if property is false it hides the button using simple fade out animation and vice-versa.
But for some unknown reason, for the first time when I perform some task which sets the bool value to false. It gives a brief flash before fading out. Upon setting it to true everything is as expected.
My style is
<Style x:Key="CustomButtonStyle" TargetType="{x:Type Button}" >
<Style.Triggers>
<DataTrigger Binding="{Binding IsButtonVisible}" Value="true">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard Storyboard.TargetProperty="Opacity" >
<DoubleAnimation Duration="0:0:0.2" To="1"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<BeginStoryboard>
<Storyboard Storyboard.TargetProperty="Opacity" >
<DoubleAnimation Duration="0:0:0.2" To="0"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.ExitActions>
</DataTrigger>
<Trigger Property="Opacity" Value="0">
<Setter Property="Visibility" Value="Collapsed"></Setter>
</Trigger>
<Trigger Property="Opacity" Value="1" >
<Setter Property="Visibility" Value="Visible"></Setter>
</Trigger>
</Style.Triggers>
<Style.Setters>
<Setter Property="Height" Value="93"/>
<Setter Property="Width" Value="93"/>
<Setter Property="Opacity" Value="0"/>
</Style.Setters>
</Style>
Note:-
Default value of IsButtonVisible is true
Why do you have two DataTrigger? Try this solution, maybe it solves your problem.
<Style x:Key="DefaultButtonStyle" TargetType="{x:Type Button}">
<Style.Triggers>
<DataTrigger Binding="{Binding IsVisible}" Value="true">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard FillBehavior="HoldEnd" Storyboard.TargetProperty="Opacity">
<DoubleAnimation Duration="0:0:0.2" To="1"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<BeginStoryboard>
<Storyboard Storyboard.TargetProperty="Opacity" >
<DoubleAnimation Duration="0:0:0.2" To="0" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.ExitActions>
</DataTrigger>
<Trigger Property="Opacity" Value="0">
<Setter Property="Visibility" Value="Collapsed"></Setter>
</Trigger>
<Trigger Property="Opacity" Value="1">
<Setter Property="Visibility" Value="Visible"></Setter>
</Trigger>
</Style.Triggers>
<Style.Setters>
<Setter Property="Height" Value="93"/>
<Setter Property="Width" Value="93"/>
<Setter Property="Opacity" Value="0"/>
</Style.Setters>
</Style>
Edit:
XAML
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApplication1"
mc:Ignorable="d"
Title="MainWindow" Height="279.716" Width="279.784"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
<Window.Resources>
<Style x:Key="ButtonStyleTrue" TargetType="{x:Type Button}">
<Style.Triggers>
<DataTrigger Binding="{Binding VisibleTrue}" Value="true">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard FillBehavior="HoldEnd" Storyboard.TargetProperty="Opacity">
<DoubleAnimation Duration="0:0:0.2" To="1"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<BeginStoryboard>
<Storyboard Storyboard.TargetProperty="Opacity" >
<DoubleAnimation Duration="0:0:0.2" To="0" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.ExitActions>
</DataTrigger>
<Trigger Property="Opacity" Value="0">
<Setter Property="Visibility" Value="Collapsed"></Setter>
</Trigger>
<Trigger Property="Opacity" Value="1">
<Setter Property="Visibility" Value="Visible"></Setter>
</Trigger>
</Style.Triggers>
<Style.Setters>
<Setter Property="Height" Value="93"/>
<Setter Property="Width" Value="93"/>
<Setter Property="Opacity" Value="0"/>
</Style.Setters>
</Style>
<Style x:Key="ButtonStyleFalse" TargetType="{x:Type Button}">
<Style.Triggers>
<DataTrigger Binding="{Binding VisibleFalse}" Value="true">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard FillBehavior="HoldEnd" Storyboard.TargetProperty="Opacity">
<DoubleAnimation Duration="0:0:0.2" To="1"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<BeginStoryboard>
<Storyboard Storyboard.TargetProperty="Opacity" >
<DoubleAnimation Duration="0:0:0.2" To="0" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.ExitActions>
</DataTrigger>
<Trigger Property="Opacity" Value="0">
<Setter Property="Visibility" Value="Collapsed"></Setter>
</Trigger>
<Trigger Property="Opacity" Value="1">
<Setter Property="Visibility" Value="Visible"></Setter>
</Trigger>
</Style.Triggers>
<Style.Setters>
<Setter Property="Height" Value="93"/>
<Setter Property="Width" Value="93"/>
<Setter Property="Opacity" Value="0"/>
</Style.Setters>
</Style>
</Window.Resources>
<Grid>
<Button Content="Button" HorizontalAlignment="Left" Margin="187,11,0,0" VerticalAlignment="Top" Width="75" Click="Button_OnClick"/>
<Button Style="{StaticResource ButtonStyleFalse}" Content="Button" HorizontalAlignment="Left" Margin="47,66,0,0" VerticalAlignment="Top" Width="75"/>
<TextBlock x:Name="textBlock_true" HorizontalAlignment="Left" Margin="193,68,0,0" TextWrapping="Wrap" Text="TextBlock" VerticalAlignment="Top"/>
<Button Style="{StaticResource ButtonStyleTrue}" Content="Button" HorizontalAlignment="Left" Margin="47,143,0,0" VerticalAlignment="Top" Width="75"/>
<TextBlock x:Name="textBlock_false" HorizontalAlignment="Left" Margin="193,145,0,0" TextWrapping="Wrap" Text="TextBlock" VerticalAlignment="Top"/>
</Grid>
Code Behind
using System.ComponentModel;
using System.Windows;
namespace WpfApplication1
{
public partial class MainWindow : Window, INotifyPropertyChanged
{
/// <summary>
/// Initalvalue is true;
/// </summary>
public bool VisibleTrue
{
get { return _VisibleTrue; }
set
{
if (_VisibleTrue == value) return;
_VisibleTrue = value;
textBlock_true.Text = value ? "True" : "False";
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("VisibleTrue"));
}
}
private bool _VisibleTrue = true;
/// <summary>
/// Initialvalue is false
/// </summary>
public bool VisibleFalse
{
get { return _VisibleFalse; }
set
{
if (_VisibleFalse == value) return;
_VisibleFalse = value;
textBlock_false.Text = value ? "True" : "False";
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("VisibleFalse"));
}
}
private bool _VisibleFalse;
public MainWindow()
{
InitializeComponent();
textBlock_true.Text = "Application started";
textBlock_false.Text = "Application started";
}
public event PropertyChangedEventHandler PropertyChanged;
private void Button_OnClick(object sender, RoutedEventArgs e)
{
VisibleTrue = !VisibleTrue;
VisibleFalse = !VisibleTrue;
}
}
}
Preview
To prevent setting your bound property IsButtonVisible while animation performs you could add the following to your ViewModel:
private bool _animationInProgress = false;
private readonly object _animationLock = new object();
private bool _isButtonVisible = true;
public bool IsButtonVisible {
get { return _isButtonVisible; }
set {
lock (_animationLock) {
if (value == _isButtonVisible || _animationInProgress) return;
_animationInProgress = true;
}
_isButtonVisible = value;
OnPropertyChanged();
Timer animationDoneTimer = new Timer {Interval = 200, Enabled = true, AutoReset = false};
animationDoneTimer.Elapsed += (sender, args) => {
lock (_animationLock) {
_animationInProgress = false;
}
};
}
}
As second thing I would remove the Trigger:
<Trigger Property="Opacity" Value="1" >
<Setter Property="Visibility" Value="Visible"></Setter>
</Trigger>
For the behaviour, that you'd like to set Visibility to Collapsed on Opacity == 0 and Visibility to Visible on Opacity =! 0 you should do it this way:
...
<Trigger Property="Opacity" Value="0">
<Setter Property="Visibility" Value="Collapsed" />
</Trigger>
<!-- Trigger removed -->
</Style.Triggers>
<Style.Setters>
<!-- Visibilities value if Trigger Opacity == 0 does not fire -->
<Setter Property="Visibility" Value="Visible" />
<Setter Property="Height" Value="93"/>
<Setter Property="Width" Value="93"/>
<Setter Property="Opacity" Value="0"/>
</Style.Setters>
</Style>

Controlling DataTrigger ExitActions

I have a CheckBox (placed above an image) whose Opacity is set to 0 by default. When IsMouseOver is true, the CheckBox Opacity property is animated from 0 to 1 and reversed when IsMouseOver is false. However, if the CheckBox IsSelected is true, then the reverse animation(i.e DataTrigger.ExitActions) should not execute. i.e CheckBox should remain visible if it is checked. In my XAML the checkbox is disappearing even when it is checked.
Following is my XAML:
<CheckBox VerticalAlignment="Center" HorizontalAlignment="Center">
<CheckBox.Style>
<Style TargetType="{x:Type CheckBox}">
<Setter Property="Opacity" Value="0" />
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=imageGrid, Path=IsMouseOver}" Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard Timeline.DesiredFrameRate="100">
<DoubleAnimation Storyboard.TargetProperty="Opacity" From="0" To="1" Duration="00:00:00.400" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<BeginStoryboard>
<Storyboard Timeline.DesiredFrameRate="100">
<DoubleAnimation Storyboard.TargetProperty="Opacity" From="1" To="0" Duration="00:00:00.400" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.ExitActions>
</DataTrigger>
<DataTrigger Binding="{Binding IsChecked, RelativeSource={RelativeSource Mode=Self}}" Value="true">
<Setter Property="Opacity" Value="1" />
</DataTrigger>
</Style.Triggers>
</Style>
</CheckBox.Style>
</CheckBox>
Implemented the way #feO2x suggested. However it is not working as expected. Got it work in a simple application. However I intend to use the checkbox in a ListViewItem. Following is my XAML:
<Style x:Key="ThumbView_ItemContainerStyle" TargetType="{x:Type ListViewItem}">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="HorizontalContentAlignment" Value="Center"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
<Setter Property="Padding" Value="0"/>
<Setter Property="Height" Value="175" />
<Setter Property="Width" Value="125" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListViewItem}">
<Grid Background="Pink" x:Name="mygrid">
<Rectangle Name="LBRect" Fill="Transparent" Opacity="0.195" />
<Image Name="posterImg" Margin="0,0,0,0" RenderOptions.BitmapScalingMode="HighQuality" SnapsToDevicePixels="True" HorizontalAlignment="Center" VerticalAlignment="Center" Source="{Binding Path=ProfilePic}" Height="125" MaxWidth="125" />
<CheckBox IsChecked="{Binding IsSelected, Mode=TwoWay}" VerticalAlignment="Center" HorizontalAlignment="Center" x:Name="CheckBox">
<CheckBox.RenderTransform>
<ScaleTransform ScaleX="1.1" ScaleY="1.1" />
</CheckBox.RenderTransform>
<CheckBox.Style>
<Style TargetType="{x:Type CheckBox}" BasedOn="{StaticResource ThumbViewCheckBoxStyle}">
<Setter Property="Opacity" Value="0" />
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding Path=IsMouseOver, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ListViewItem}}}" Value="False" />
<Condition Binding="{Binding ElementName=CheckBox, Path=IsChecked}" Value="False" />
</MultiDataTrigger.Conditions>
<MultiDataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Opacity" To="0.0" Duration="0:0:0.4" />
</Storyboard>
</BeginStoryboard>
</MultiDataTrigger.EnterActions>
<MultiDataTrigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Opacity" To="1.0" Duration="0:0:0.4" />
</Storyboard>
</BeginStoryboard>
</MultiDataTrigger.ExitActions>
</MultiDataTrigger>
</Style.Triggers>
</Style>
</CheckBox.Style>
</CheckBox>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
This can be achieved with IMultiValueConverter in place. Pass two bindings to it i.e. IsMouseOver and IsChecked.
Converter:
public class MyConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter,
CultureInfo culture)
{
bool isMouseOver = (bool)values[0];
bool isChecked = (bool)values[1];
return isChecked || isMouseOver;
}
public object[] ConvertBack(object value, Type[] targetTypes,
object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
XAML:
<DataTrigger Value="True">
<DataTrigger.Binding>
<MultiBinding Converter="{StaticResource MyConverter}">
<Binding Path="IsMouseOver" ElementName="imageGrid"/>
<Binding Path="IsChecked" RelativeSource="{RelativeSource Self}"/>
</MultiBinding>
</DataTrigger.Binding>
<!-- Rest same trigger -->
</DataTrigger>
Of course you need to add MyValueConverter instance as resource in your XAML file.
Update February 26th: In your Style that is not working, you reference the checkbox the wrong way. ElementName can only be used in a binding if the target control was given a name and is accessible within the current WPF XAML namescope (you can learn about XAML namescopes here).
You should use a relative source within your binding to reference the CheckBox correctly: RelativeSource={RelativeSource Self}. The following ListView style shows the whole example:
<Style x:Key="ListViewItemStyle" TargetType="{x:Type ListViewItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Border Background="{TemplateBinding Background}">
<Grid Margin="{TemplateBinding Padding}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="150" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding Name}" TextTrimming="CharacterEllipsis" />
<CheckBox IsChecked="{Binding IsSelected}" Grid.Column="1">
<CheckBox.Style>
<Style TargetType="{x:Type CheckBox}">
<Setter Property="Opacity" Value="0.0" />
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding Path=IsMouseOver, RelativeSource={RelativeSource AncestorType=ListBoxItem}}"
Value="False" />
<Condition Binding="{Binding Path=IsChecked, RelativeSource={RelativeSource Self}}"
Value="False" />
</MultiDataTrigger.Conditions>
<MultiDataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Opacity"
To="0.0" Duration="0:0:0.4" />
</Storyboard>
</BeginStoryboard>
</MultiDataTrigger.EnterActions>
<MultiDataTrigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Opacity"
To="1.0" Duration="0:0:0.4" />
</Storyboard>
</BeginStoryboard>
</MultiDataTrigger.ExitActions>
</MultiDataTrigger>
</Style.Triggers>
</Style>
</CheckBox.Style>
</CheckBox>
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
You can download a working example from here (DropBox link).
Original Answer:
You can use a MultiDataTrigger within your style to accomplish the behavior you want. Have a look at the following code:
<CheckBox Grid.Column="1" VerticalAlignment="Center" HorizontalAlignment="Center" Content="Check Me" Name="CheckBox">
<CheckBox.Style>
<Style TargetType="{x:Type CheckBox}">
<Setter Property="Opacity" Value="0.0" />
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding ElementName=Rectangle, Path=IsMouseOver}" Value="False" />
<Condition Binding="{Binding ElementName=CheckBox, Path=IsChecked}" Value="False" />
</MultiDataTrigger.Conditions>
<MultiDataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Opacity"
To="0.0"
Duration="0:0:0.4"
BeginTime="0:0:0.5" />
</Storyboard>
</BeginStoryboard>
</MultiDataTrigger.EnterActions>
<MultiDataTrigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Opacity"
To="1.0"
Duration="0:0:0.4" />
</Storyboard>
</BeginStoryboard>
</MultiDataTrigger.ExitActions>
</MultiDataTrigger>
</Style.Triggers>
</Style>
</CheckBox.Style>
</CheckBox>
A MultiDataTrigger is used to describe two conditions that have to be true for the enter actions to be executed. In this case I choose that the IsMouseOver property of a rectangle as well as the IsChecked property of the checkbox must both be false. Thus the checkbox is faded in when the mouse is over the rectangle and stays visible when it becomes checked.
You can download my full sample here (it's a Dropbox link).
Hope this helps you. If you have any questions, please feel free to leave a comment.

WPF: How to smoothly stop a rotation animation with the DataTrigger

The animation hardly stops and the image resets its position but I want the animation to finish to the next full 360°.
Any ideas on that?
The XAML code:
<Button
BorderThickness="0"
Cursor="Hand"
Command="{Binding RefreshCommand}">
<Button.Template>
<ControlTemplate TargetType="Button">
<Border>
<ContentPresenter />
</Border>
</ControlTemplate>
</Button.Template>
<Image Source="../Resources/RefreshIcon.png">
<Image.Style>
<Style>
<Setter Property="Image.RenderTransform">
<Setter.Value>
<RotateTransform />
</Setter.Value>
</Setter>
<Setter Property="Image.RenderTransformOrigin" Value=".5,.5">
</Setter>
<Style.Triggers>
<DataTrigger Binding="{Binding IsRefreshing}" Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard Name="RotationStoryboard">
<Storyboard>
<DoubleAnimation
Storyboard.TargetProperty="RenderTransform.Angle"
From="0"
To="360"
Duration="0:0:0.8"
RepeatBehavior="Forever" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
</DataTrigger>
<DataTrigger Binding="{Binding IsRefreshing}" Value="False">
<DataTrigger.EnterActions>
<RemoveStoryboard BeginStoryboardName="RotationStoryboard"/>
</DataTrigger.EnterActions>
</DataTrigger>
</Style.Triggers>
</Style>
</Image.Style>
</Image>
</Button>
The final solution except the fact that it has to be put into a UserControl to make it more generic (also see #Sheridans post and my comment below):
<Button
BorderThickness="0"
Cursor="Hand"
Command="{Binding RefreshCommand}">
<Button.Template>
<ControlTemplate TargetType="Button">
<Border>
<ContentPresenter />
</Border>
</ControlTemplate>
</Button.Template>
<Image
Name="RefreshImage"
Source="../Resources/RefreshIcon.png"
RenderTransformOrigin=".5,.5">
<Image.Resources>
<Storyboard
x:Key="RotationStoryboard"
Completed="RotationStoryboardCompleted">
<DoubleAnimation
Storyboard.Target="{Binding ElementName=RefreshImage}"
Storyboard.TargetProperty="RenderTransform.Angle"
From="0"
To="360"
Duration="0:0:1.5">
<DoubleAnimation.EasingFunction>
<CircleEase EasingMode="EaseInOut"></CircleEase>
</DoubleAnimation.EasingFunction>
</DoubleAnimation>
</Storyboard>
</Image.Resources>
<Image.Style>
<Style>
<Setter Property="Image.RenderTransform">
<Setter.Value>
<RotateTransform />
</Setter.Value>
</Setter>
<Setter Property="Image.RenderTransformOrigin" Value=".5,.5">
</Setter>
<Style.Triggers>
<DataTrigger Binding="{Binding IsRefreshing}" Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard>
<StaticResource ResourceKey="RotationStoryboard"/>
</BeginStoryboard>
</DataTrigger.EnterActions>
</DataTrigger>
</Style.Triggers>
</Style>
</Image.Style>
</Image>
</Button>
Code behind:
public partial class SomeView
{
public SomeView()
{
InitializeComponent();
}
private void RotationStoryboardCompleted(object sender, EventArgs e)
{
var viewModel = (ISomeViewModel)DataContext;
var storyboard = (Storyboard)((ClockGroup)sender).Timeline;
if (viewModel.IsRefreshing)
{
storyboard.Begin();
}
}
}
I don't know of any way to do this in XAML, but with code, there is a Timeline.Completed Event that you can use. If you attach a handler to your Storyboard, then it will get called when it is finished and you can do whatever you want:
<BeginStoryboard>
<Storyboard Name="RotationStoryboard" Completed="StoryboardCompleted">
<DoubleAnimation Storyboard.TargetProperty="RenderTransform.Angle" From="0"
To="360" Duration="0:0:0.8" />
</Storyboard>
</BeginStoryboard>
In code:
private void StoryboardCompleted(object sender, EventArgs e)
{
// Restart your Storyboard here each time until you want it to stop.
}
For further help, please see the example in the linked page on MSDN.

How to stop the treeview collaps event after mouse click

How can I stop the treeview collaps event after mouse click on TreeViewItem arrow (on view)?
I need to show my treeview expanded all time.
You could set the Collapsed event in XAML:
<TreeView
Name="myTreeView"
ItemsSource="{Binding dataSource}">
<TreeView.ItemContainerStyle>
<Style TargetType="TreeViewItem">
<Setter Property="IsExpanded" Value="True" />
<Style.Triggers>
<EventTrigger RoutedEvent="Collapsed">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard>
<BooleanAnimationUsingKeyFrames
Duration="0"
Storyboard.TargetProperty="(TreeViewItem.IsExpanded)">
<DiscreteBooleanKeyFrame KeyTime="0" Value="True" />
</BooleanAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
</Style.Triggers>
</Style>
</TreeView.ItemContainerStyle>
</TreeView>
You can set the Collapsed event on the TreeViewItem to this:
private void TreeViewItem_Collapsed(object sender, RoutedEventArgs e)
{
(sender as TreeViewItem).IsExpanded = true;
}
It doesn't prevent it from collapsing however, it just automatically expands it whenever it's collapsed.
Just retemplate the TreeViewItems to not even have an arrow (and collapsible area).
e.g.
<Style TargetType="TreeViewItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="TreeViewItem">
<StackPanel>
<ContentPresenter ContentSource="Header"/>
<ItemsPresenter Margin="20,0,0,0"/>
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
(This is the bare minimum, you will want to have triggers to show the current selection if you need that)

Resources