Execute Animation DataTrigger with binding to Command - c#

I tried to execute animation with DataTrigger but it don't reise up.
Program don't throw any exception and errors.
Can someone explain why animation don't Start ?
Here is my storyboard
<Storyboard x:Key="storyboard2" AutoReverse="True" RepeatBehavior="Forever" Duration="00:01:00">
<DoubleAnimation Storyboard.TargetProperty="(Rectangle.RenderTransform).(ScaleTransform.ScaleX)" Storyboard.Target="{Binding Source={x:Reference RectangleScaleFrom}}" To="4" From="1"/>
<DoubleAnimation Storyboard.TargetProperty="(Rectangle.RenderTransform).(ScaleTransform.ScaleY)" Storyboard.Target="{Binding Source={x:Reference RectangleScaleFrom}}"
From="4" To="1"/>
</Storyboard>"
Here Is DataTrigger to start and Stop Animation
<DataTrigger Binding="{Binding RunAnimation}" Value="True" x:Key="Start">
<DataTrigger.EnterActions>
<BeginStoryboard x:Name="BeginScaleFrom" Storyboard="{StaticResource storyboard2}"/>
<!--<BeginStoryboard x:Name="BeginScaleTo" Storyboard="{StaticResource storyboard2}"/>-->
</DataTrigger.EnterActions>
</DataTrigger>
<DataTrigger Binding="{Binding RunAnimation}" Value="False" x:Key="Stop">
<DataTrigger.ExitActions>
<StopStoryboard BeginStoryboardName="BeginScaleFrom"/>
<!--<StopStoryboard BeginStoryboardName="BeginScaleTo"/>-->
</DataTrigger.ExitActions>
</DataTrigger>
Here is Button
<ToggleButton Grid.Row="2" VerticalAlignment="Center" HorizontalAlignment="Center" Margin="50,20,50,30" FontSize="20" x:Name="StartButton" Style="{StaticResource StartStopButtonStyle}" Width="100" Height="50" Command="{Binding StartAnimation}">
</ToggleButton>
And Command to start
private bool _StartAnim = false;
public bool RunAnimation
{
get => _StartAnim;
set
{
_StartAnim = value;
RaisePropertyChanged(nameof(RunAnimation));
}
}
public RelayCommand StartAnimation { get; private set; }
public MainViewModel()
{
StartAnimation = new RelayCommand(() => RunAnimation = !RunAnimation);
}

Something like this should work:
<Window.Resources>
<Storyboard x:Key="storyboard2">
<DoubleAnimation Storyboard.TargetProperty="RenderTransform.ScaleX"
To="4" From="1" AutoReverse="True"
RepeatBehavior="Forever" Duration="00:00:10"/>
<DoubleAnimation Storyboard.TargetProperty="RenderTransform.ScaleY"
From="4" To="1" AutoReverse="True"
RepeatBehavior="Forever" Duration="00:00:10"/>
</Storyboard>
</Window.Resources>
<Rectangle Width="100" Height="100" Fill="Red">
<Rectangle.Style>
<Style TargetType="Rectangle">
<Setter Property="RenderTransform">
<Setter.Value>
<ScaleTransform/>
</Setter.Value>
</Setter>
<Style.Triggers>
<DataTrigger Binding="{Binding RunAnimation}" Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard x:Name="BeginScaleFrom"
Storyboard="{StaticResource storyboard2}"/>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<StopStoryboard BeginStoryboardName="BeginScaleFrom"/>
</DataTrigger.ExitActions>
</DataTrigger>
</Style.Triggers>
</Style>
</Rectangle.Style>
</Rectangle>

Related

Why is my WPF PasswordBox style trigger not working?

So I have this PasswordBox in my app.
XAML
<PasswordBox Name="PB_PASSWORD" Padding="100,0,34,0" FontSize="20" Width="384" Height="34" PasswordChar="█" Password="" HorizontalAlignment="Left" VerticalAlignment="Top" FontFamily="Century Gothic" HorizontalContentAlignment="Left" VerticalContentAlignment="Center" TabIndex="2" PasswordChanged="PB_PASSWORD_PasswordChanged" >
<PasswordBox.Style>
<Style BasedOn="{x:Null}" TargetType="{x:Type PasswordBox}">
<Setter Property="Background" Value="#FFCCCCCC" />
<Setter Property="Foreground" Value="#FFF22613" />
<Setter Property="BorderBrush" Value="#FFF22613" />
<Setter Property="BorderThickness" Value="0,2,0,2" />
<Setter Property="ClipToBounds" Value="true"/>
<Setter Property="OverridesDefaultStyle" Value="true"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="PasswordBox">
<Grid>
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<ScrollViewer x:Name="PART_ContentHost" Margin="0,-4,0,0" />
</Border>
<TextBlock Name="TB" Text="Password" HorizontalAlignment="Left" Margin="140,0,0,0" VerticalAlignment="Center" Foreground="#FF222222" Opacity="0.3"/>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsFocused" Value="true">
<Setter TargetName="TB" Property="Text" Value="Password:" />
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<ThicknessAnimation Storyboard.TargetProperty="Padding" To="100,0,34,0" Duration="0:0:0.3">
<ThicknessAnimation.EasingFunction>
<SineEase EasingMode="EaseOut"/>
</ThicknessAnimation.EasingFunction>
</ThicknessAnimation>
<ThicknessAnimation Storyboard.TargetName="TB" Storyboard.TargetProperty="Margin" To="2,0,0,0" Duration="0:0:0.3">
<ThicknessAnimation.EasingFunction>
<SineEase EasingMode="EaseOut"/>
</ThicknessAnimation.EasingFunction>
</ThicknessAnimation>
<DoubleAnimation Storyboard.TargetName="TB" Storyboard.TargetProperty="Opacity" To="1" Duration="0:0:0.3">
<DoubleAnimation.EasingFunction>
<SineEase EasingMode="EaseOut"/>
</DoubleAnimation.EasingFunction>
</DoubleAnimation>
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
<Trigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<ThicknessAnimation Storyboard.TargetProperty="Padding" To="230,0,0,0" Duration="0:0:0.3">
<ThicknessAnimation.EasingFunction>
<BackEase EasingMode="EaseOut"/>
</ThicknessAnimation.EasingFunction>
</ThicknessAnimation>
<ThicknessAnimation Storyboard.TargetName="TB" Storyboard.TargetProperty="Margin" To="140,0,0,0" Duration="0:0:0.3">
<ThicknessAnimation.EasingFunction>
<BackEase EasingMode="EaseOut"/>
</ThicknessAnimation.EasingFunction>
</ThicknessAnimation>
<DoubleAnimation Storyboard.TargetName="TB" Storyboard.TargetProperty="Opacity" To="0.3" Duration="0:0:0.3">
<DoubleAnimation.EasingFunction>
<BackEase EasingMode="EaseOut"/>
</DoubleAnimation.EasingFunction>
</DoubleAnimation>
</Storyboard>
</BeginStoryboard>
</Trigger.ExitActions>
</Trigger>
<Trigger Property="ClipToBounds" Value="false">
<Setter TargetName="TB" Property="Text" Value="Password:" />
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<ThicknessAnimation Storyboard.TargetProperty="Padding" To="100,0,34,0" Duration="0:0:0.3">
<ThicknessAnimation.EasingFunction>
<SineEase EasingMode="EaseOut"/>
</ThicknessAnimation.EasingFunction>
</ThicknessAnimation>
<ThicknessAnimation Storyboard.TargetName="TB" Storyboard.TargetProperty="Margin" To="2,0,0,0" Duration="0:0:0.3">
<ThicknessAnimation.EasingFunction>
<SineEase EasingMode="EaseOut"/>
</ThicknessAnimation.EasingFunction>
</ThicknessAnimation>
<DoubleAnimation Storyboard.TargetName="TB" Storyboard.TargetProperty="Opacity" To="1" Duration="0:0:0.3">
<DoubleAnimation.EasingFunction>
<SineEase EasingMode="EaseOut"/>
</DoubleAnimation.EasingFunction>
</DoubleAnimation>
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
<Trigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<ThicknessAnimation Storyboard.TargetProperty="Padding" To="230,0,0,0" Duration="0:0:0.3">
<ThicknessAnimation.EasingFunction>
<BackEase EasingMode="EaseOut"/>
</ThicknessAnimation.EasingFunction>
</ThicknessAnimation>
<ThicknessAnimation Storyboard.TargetName="TB" Storyboard.TargetProperty="Margin" To="140,0,0,0" Duration="0:0:0.3">
<ThicknessAnimation.EasingFunction>
<BackEase EasingMode="EaseOut"/>
</ThicknessAnimation.EasingFunction>
</ThicknessAnimation>
<DoubleAnimation Storyboard.TargetName="TB" Storyboard.TargetProperty="Opacity" To="0.3" Duration="0:0:0.3">
<DoubleAnimation.EasingFunction>
<BackEase EasingMode="EaseOut"/>
</DoubleAnimation.EasingFunction>
</DoubleAnimation>
</Storyboard>
</BeginStoryboard>
</Trigger.ExitActions>
</Trigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsFocused" Value="false" />
<Condition Property="ClipToBounds" Value="true" />
</MultiTrigger.Conditions>
<Setter TargetName="TB" Property="Text" Value="Password" />
<MultiTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<ThicknessAnimation Storyboard.TargetProperty="Padding" To="230,0,0,0" Duration="0:0:0.3">
<ThicknessAnimation.EasingFunction>
<BackEase EasingMode="EaseOut"/>
</ThicknessAnimation.EasingFunction>
</ThicknessAnimation>
<ThicknessAnimation Storyboard.TargetName="TB" Storyboard.TargetProperty="Margin" To="140,0,0,0" Duration="0:0:0.3">
<ThicknessAnimation.EasingFunction>
<BackEase EasingMode="EaseOut"/>
</ThicknessAnimation.EasingFunction>
</ThicknessAnimation>
<DoubleAnimation Storyboard.TargetName="TB" Storyboard.TargetProperty="Opacity" To="0.3" Duration="0:0:0.3">
<DoubleAnimation.EasingFunction>
<BackEase EasingMode="EaseOut"/>
</DoubleAnimation.EasingFunction>
</DoubleAnimation>
</Storyboard>
</BeginStoryboard>
</MultiTrigger.EnterActions>
<MultiTrigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<ThicknessAnimation Storyboard.TargetProperty="Padding" To="100,0,34,0" Duration="0:0:0.3">
<ThicknessAnimation.EasingFunction>
<SineEase EasingMode="EaseOut"/>
</ThicknessAnimation.EasingFunction>
</ThicknessAnimation>
<ThicknessAnimation Storyboard.TargetName="TB" Storyboard.TargetProperty="Margin" To="2,0,0,0" Duration="0:0:0.3">
<ThicknessAnimation.EasingFunction>
<SineEase EasingMode="EaseOut"/>
</ThicknessAnimation.EasingFunction>
</ThicknessAnimation>
<DoubleAnimation Storyboard.TargetName="TB" Storyboard.TargetProperty="Opacity" To="1" Duration="0:0:0.3">
<DoubleAnimation.EasingFunction>
<SineEase EasingMode="EaseOut"/>
</DoubleAnimation.EasingFunction>
</DoubleAnimation>
</Storyboard>
</BeginStoryboard>
</MultiTrigger.ExitActions>
</MultiTrigger>
<Trigger Property="IsEnabled" Value="false">
<Setter TargetName="TB" Property="Text" Value="DISABLE"/>
<Setter TargetName="TB" Property="Margin" Value="140,0,0,0"/>
<Setter Property="Background" Value="#FFAAAAAA"/>
<Setter Property="Foreground" Value="#FF777777"/>
<Setter Property="BorderBrush" Value="#FF888888" />
<Setter Property="BorderThickness" Value="0,3,0,3" />
</Trigger>
<Trigger Property="Tag" Value="ShowPW">
<Setter Property="Visibility" Value="Hidden"/>
</Trigger>
<Trigger Property="Tag" Value="HidePW">
<Setter Property="Visibility" Value="Visible"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
</Style.Triggers>
</Style>
</PasswordBox.Style>
</PasswordBox>
And I need to change its border color when the password entered is empty.
Following Code Changes BorderColor if PasswordBox's Password Value Changed.
Note: PasswordBox Disabling is Happening After PasswordBox.Password is Cleared. So this Shouldn't Matter [I Guess].
c#
private void PB_PASSWORD_PasswordChanged(object sender, RoutedEventArgs e)
{
if (PB_PASSWORD.SecurePassword.Length == 0)
{ //Password is Empty.
PB_PASSWORD.ClipToBounds = true;
}
else
{ //Password Not Empty
PB_PASSWORD.ClipToBounds = false;
}
Int32 PWStrength = 0;
if (PB_PASSWORD.SecurePassword.Length >= 5)
{
//A Function that Return int Value between 0-5 depending on how Strong is Password.
PWStrength = GetPasswordStrength(Marshal.PtrToStringUni(Marshal.SecureStringToGlobalAllocUnicode(PB_PASSWORD.SecurePassword)));
}
//Corresponding Colors Are Set as per Returned Integer0=red, 1=Orange+Red, 2=Orange, 3=Yellow, 4=Light Green, 5=Green
switch (PWStrength)
{
case 0:
{
//Following 2 Lines Required to Unfreez Color From Control
PB_PASSWORD.Foreground = new SolidColorBrush(CustomColors.PasswordStrengthColors[PWStrength]);
PB_PASSWORD.BorderBrush = new SolidColorBrush(CustomColors.PasswordStrengthColors[PWStrength]);
ColorAnimation AnimateForegroundColor_0 = new ColorAnimation(CustomColors.PasswordStrengthColors[PWStrength], new Duration(TimeSpan.FromMilliseconds(200)));
PB_PASSWORD.Foreground.BeginAnimation(SolidColorBrush.ColorProperty, AnimateForegroundColor_0);
ColorAnimation AnimateBorderBrushColor_0 = new ColorAnimation(CustomColors.PasswordStrengthColors[PWStrength], new Duration(TimeSpan.FromMilliseconds(200)));
PB_PASSWORD.BorderBrush.BeginAnimation(SolidColorBrush.ColorProperty, AnimateBorderBrushColor_0);
break;
}
default:
{
ColorAnimation AnimateForegroundColor = new ColorAnimation(CustomColors.PasswordStrengthColors[PWStrength], new Duration(TimeSpan.FromMilliseconds(200)));
PB_PASSWORD.Foreground.BeginAnimation(SolidColorBrush.ColorProperty, AnimateForegroundColor);
ColorAnimation AnimateBorderBrushColor = new ColorAnimation(CustomColors.PasswordStrengthColors[PWStrength], new Duration(TimeSpan.FromMilliseconds(200)));
PB_PASSWORD.BorderBrush.BeginAnimation(SolidColorBrush.ColorProperty, AnimateBorderBrushColor);
break;
}
}
}
The password value is not accessible, so I used the ClipToBounds Boolean value to set it, like this:
C#
if (String.IsNullOrEmpty(PB_PASSWORD.Password))
{ PB_PASSWORD.ClipToBounds = true; }
else
{ PB_PASSWORD.ClipToBounds = false; }
This is working fine when the app first starts.
The problem starts when I modify the enable/disable value from the code-behind, such as in the following:
C#
private void Button_Click(object sender, RoutedEventArgs e)
{
if (PB_PASSWORD.IsEnabled)
{
PB_PASSWORD.ClipToBounds = true;
PB_PASSWORD.Password = "";
BTN_BROWSE.Focus();
PB_PASSWORD.MoveFocus(new System.Windows.Input.TraversalRequest(System.Windows.Input.FocusNavigationDirection.Next));
PB_PASSWORD.IsEnabled = false;
}
else
{
PB_PASSWORD.IsEnabled = true;
}
}
It's supposed to look like this, after typing in the password and then disabling:
But it looks like this:
I need to solve it in the XAML code.
Link to project: HERE - Must download!
I have decided to provide you with a full - MVVM example, so that you can learn how to do it "the proper way".
Note: I am using MVVM Light (for it's RelayCommand). You can install it through NuGet. It's worth having, as it provides a lot of useful classes for MVVM development. Another alternative to it is Prism.
1. What is MVVM?
MVVM (Model - View - ViewModel) is a programming pattern, that goes along perfectly with WPF. It's main purpose is to detach Views - what you see, from ViewModels - your program's logic.
It can result in a little bit more coding needed, but the payoff is great - you get a clean, structured code, that is modular and VERY easily testable (namely, Unit Tests).
1.1 - Model
Model is basically the structure of your program. It should provide the backbone for your classes, that hold the Data and are further used in ViewModels.
In the case of this project - there is no model though - as its not needed (it surely will become needed as you progress with your application!)
1.2 - View
The View is basically what you see. Most often it is a Window, that has elements it displays - but it doesn't have to be only that! A UserControl can be a View in itself, and have it's own ViewModel bound to it - a different one than the Window it is in.
1.3 ViewModel
A ViewModel is basically the core of your program. It holds the logic and has properties, that the View can bind to and use/display in its' controls.
Consider this a brain of your application.
2. Code
View:
<Window x:Class="PasswordBoxMVVM.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:PasswordBoxMVVM"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:b="clr-namespace:System.Media;assembly=System"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<local:PasswordLengthToColorConverter x:Key="passwordLengthToColorConverter" />
</Window.Resources>
<Grid>
<StackPanel VerticalAlignment="Center">
<PasswordBox local:PasswordBoxMVVMAttachedProperties.EncryptedPassword="{Binding Path=Password, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
IsEnabled="{Binding Path=IsPasswordFieldDisabled, Mode=TwoWay ,UpdateSourceTrigger=PropertyChanged}"
FontSize="20" Width="384" Height="34" PasswordChar="█" HorizontalAlignment="Center" FontFamily="Century Gothic"
HorizontalContentAlignment="Left" VerticalContentAlignment="Center" TabIndex="2" Foreground="Red"
PasswordChanged="MyPasswordBox_PasswordChanged"
IsEnabledChanged="PasswordBox_IsEnabledChanged">
<i:Interaction.Triggers>
<i:EventTrigger EventName="PasswordChanged">
<i:InvokeCommandAction Command="{Binding Path=PasswordChangedCommand}" />
</i:EventTrigger>
</i:Interaction.Triggers>
<PasswordBox.Style>
<Style TargetType="{x:Type PasswordBox}">
<Setter Property="Background" Value="#FFCCCCCC" />
<Setter Property="BorderThickness" Value="0,2,0,2" />
<Setter Property="BorderBrush" Value="Red" />
<Setter Property="ClipToBounds" Value="true"/>
<Setter Property="OverridesDefaultStyle" Value="true"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="PasswordBox">
<Grid>
<Border Background="{TemplateBinding Background}"
BorderThickness="{TemplateBinding BorderThickness}"
BorderBrush="{TemplateBinding Foreground}">
<ScrollViewer x:Name="PART_ContentHost" Margin="0,-4,0,0"/>
</Border>
<TextBlock Name="TB" Text="Password" HorizontalAlignment="Left" Margin="140,0,0,0" VerticalAlignment="Center" Opacity="0.3" Foreground="Gray">
</TextBlock>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsFocused" Value="true">
<Setter TargetName="TB" Property="Text" Value="Password:" />
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<ThicknessAnimation Storyboard.TargetProperty="Padding" To="100,0,34,0" Duration="0:0:0.3">
<ThicknessAnimation.EasingFunction>
<SineEase EasingMode="EaseOut"/>
</ThicknessAnimation.EasingFunction>
</ThicknessAnimation>
<ThicknessAnimation Storyboard.TargetName="TB" Storyboard.TargetProperty="Margin" To="2,0,0,0" Duration="0:0:0.3">
<ThicknessAnimation.EasingFunction>
<SineEase EasingMode="EaseOut"/>
</ThicknessAnimation.EasingFunction>
</ThicknessAnimation>
<DoubleAnimation Storyboard.TargetName="TB" Storyboard.TargetProperty="Opacity" To="1" Duration="0:0:0.3">
<DoubleAnimation.EasingFunction>
<SineEase EasingMode="EaseOut"/>
</DoubleAnimation.EasingFunction>
</DoubleAnimation>
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
<Trigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<ThicknessAnimation Storyboard.TargetProperty="Padding" To="230,0,0,0" Duration="0:0:0.3">
<ThicknessAnimation.EasingFunction>
<BackEase EasingMode="EaseOut"/>
</ThicknessAnimation.EasingFunction>
</ThicknessAnimation>
<ThicknessAnimation Storyboard.TargetName="TB" Storyboard.TargetProperty="Margin" To="140,0,0,0" Duration="0:0:0.3">
<ThicknessAnimation.EasingFunction>
<BackEase EasingMode="EaseOut"/>
</ThicknessAnimation.EasingFunction>
</ThicknessAnimation>
<DoubleAnimation Storyboard.TargetName="TB" Storyboard.TargetProperty="Opacity" To="0.3" Duration="0:0:0.3">
<DoubleAnimation.EasingFunction>
<BackEase EasingMode="EaseOut"/>
</DoubleAnimation.EasingFunction>
</DoubleAnimation>
</Storyboard>
</BeginStoryboard>
</Trigger.ExitActions>
</Trigger>
<DataTrigger Binding="{Binding Path=IsPasswordFieldEmpty,Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Value="false">
<Setter TargetName="TB" Property="Text" Value="Password:" />
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<ThicknessAnimation Storyboard.TargetProperty="Padding" To="100,0,34,0" Duration="0:0:0.3">
<ThicknessAnimation.EasingFunction>
<SineEase EasingMode="EaseOut"/>
</ThicknessAnimation.EasingFunction>
</ThicknessAnimation>
<ThicknessAnimation Storyboard.TargetName="TB" Storyboard.TargetProperty="Margin" To="2,0,0,0" Duration="0:0:0.3">
<ThicknessAnimation.EasingFunction>
<SineEase EasingMode="EaseOut"/>
</ThicknessAnimation.EasingFunction>
</ThicknessAnimation>
<DoubleAnimation Storyboard.TargetName="TB" Storyboard.TargetProperty="Opacity" To="1" Duration="0:0:0.3">
<DoubleAnimation.EasingFunction>
<SineEase EasingMode="EaseOut"/>
</DoubleAnimation.EasingFunction>
</DoubleAnimation>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<ThicknessAnimation Storyboard.TargetProperty="Padding" To="230,0,0,0" Duration="0:0:0.3">
<ThicknessAnimation.EasingFunction>
<BackEase EasingMode="EaseOut"/>
</ThicknessAnimation.EasingFunction>
</ThicknessAnimation>
<ThicknessAnimation Storyboard.TargetName="TB" Storyboard.TargetProperty="Margin" To="140,0,0,0" Duration="0:0:0.3">
<ThicknessAnimation.EasingFunction>
<BackEase EasingMode="EaseOut"/>
</ThicknessAnimation.EasingFunction>
</ThicknessAnimation>
<DoubleAnimation Storyboard.TargetName="TB" Storyboard.TargetProperty="Opacity" To="0.3" Duration="0:0:0.3">
<DoubleAnimation.EasingFunction>
<BackEase EasingMode="EaseOut"/>
</DoubleAnimation.EasingFunction>
</DoubleAnimation>
</Storyboard>
</BeginStoryboard>
</DataTrigger.ExitActions>
</DataTrigger>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding Path=IsFocused, RelativeSource={RelativeSource Self}}" Value="false" />
<Condition Binding="{Binding Path=IsPasswordFieldEmpty, UpdateSourceTrigger=PropertyChanged}" Value="true" />
</MultiDataTrigger.Conditions>
<Setter TargetName="TB" Property="Text" Value="Password" />
<MultiDataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<ThicknessAnimation Storyboard.TargetProperty="Padding" To="230,0,0,0" Duration="0:0:0.3">
<ThicknessAnimation.EasingFunction>
<BackEase EasingMode="EaseOut"/>
</ThicknessAnimation.EasingFunction>
</ThicknessAnimation>
<ThicknessAnimation Storyboard.TargetName="TB" Storyboard.TargetProperty="Margin" To="140,0,0,0" Duration="0:0:0.3">
<ThicknessAnimation.EasingFunction>
<BackEase EasingMode="EaseOut"/>
</ThicknessAnimation.EasingFunction>
</ThicknessAnimation>
<DoubleAnimation Storyboard.TargetName="TB" Storyboard.TargetProperty="Opacity" To="0.3" Duration="0:0:0.3">
<DoubleAnimation.EasingFunction>
<BackEase EasingMode="EaseOut"/>
</DoubleAnimation.EasingFunction>
</DoubleAnimation>
</Storyboard>
</BeginStoryboard>
</MultiDataTrigger.EnterActions>
<MultiDataTrigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<ThicknessAnimation Storyboard.TargetProperty="Padding" To="100,0,34,0" Duration="0:0:0.3">
<ThicknessAnimation.EasingFunction>
<SineEase EasingMode="EaseOut"/>
</ThicknessAnimation.EasingFunction>
</ThicknessAnimation>
<ThicknessAnimation Storyboard.TargetName="TB" Storyboard.TargetProperty="Margin" To="2,0,0,0" Duration="0:0:0.3">
<ThicknessAnimation.EasingFunction>
<SineEase EasingMode="EaseOut"/>
</ThicknessAnimation.EasingFunction>
</ThicknessAnimation>
<DoubleAnimation Storyboard.TargetName="TB" Storyboard.TargetProperty="Opacity" To="1" Duration="0:0:0.3">
<DoubleAnimation.EasingFunction>
<SineEase EasingMode="EaseOut"/>
</DoubleAnimation.EasingFunction>
</DoubleAnimation>
</Storyboard>
</BeginStoryboard>
</MultiDataTrigger.ExitActions>
</MultiDataTrigger>
<Trigger Property="IsEnabled" Value="false">
<Setter TargetName="TB" Property="Text" Value="DISABLE"/>
<Setter TargetName="TB" Property="Margin" Value="140,0,0,0"/>
<Setter Property="Background" Value="#FFAAAAAA"/>
<Setter Property="Foreground" Value="Gray"/>
<Setter Property="BorderBrush" Value="#FF888888" />
<Setter Property="BorderThickness" Value="0,3,0,3" />
</Trigger>
<Trigger Property="Tag" Value="ShowPW">
<Setter Property="Visibility" Value="Hidden"/>
</Trigger>
<Trigger Property="Tag" Value="HidePW">
<Setter Property="Visibility" Value="Visible"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
</Style.Triggers>
</Style>
</PasswordBox.Style>
<PasswordBox.Triggers>
<EventTrigger RoutedEvent="PasswordBox.PasswordChanged">
<BeginStoryboard>
<Storyboard>
<ColorAnimation Storyboard.TargetProperty="(PasswordBox.Foreground).(SolidColorBrush.Color)"
To="{Binding Path=Password.Length, UpdateSourceTrigger=PropertyChanged,
Converter={StaticResource passwordLengthToColorConverter}}" Duration="0:0:0.1">
</ColorAnimation>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="local:PasswordBoxAttachedEvent.HasBeenDisabled">
<BeginStoryboard>
<Storyboard>
<ColorAnimation Storyboard.TargetProperty="(PasswordBox.Foreground).(SolidColorBrush.Color)"
To="Gray" Duration="0:0:0.1">
</ColorAnimation>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="local:PasswordBoxAttachedEvent.HasBeenEnabled">
<BeginStoryboard>
<Storyboard>
<ColorAnimation Storyboard.TargetProperty="(PasswordBox.Foreground).(SolidColorBrush.Color)"
To="{Binding Path=Password.Length, UpdateSourceTrigger=PropertyChanged,
Converter={StaticResource passwordLengthToColorConverter}}" Duration="0:0:0.1">
</ColorAnimation>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</PasswordBox.Triggers>
</PasswordBox>
<Button Width="200" Height="50" Margin="0,50,0,0" Command="{Binding Path=ClickCommand}">Click me</Button>
</StackPanel>
</Grid>
This isn't much different from what you have provided, but it had been warped into MVVM - bindings, commands and converters were added. They are required to Bind (connect) our View and ViewModel.
The View's code behind:
public partial class MainWindow : Window
{
public MainWindow()
{
var vm = new PasswordViewModel();
this.DataContext = vm;
InitializeComponent();
}
private void MyPasswordBox_PasswordChanged(object sender, RoutedEventArgs e)
{
PasswordBox pBox = sender as PasswordBox;
PasswordBoxMVVMAttachedProperties.SetEncryptedPassword(pBox, pBox.SecurePassword);
}
private void PasswordBox_IsEnabledChanged(object sender, DependencyPropertyChangedEventArgs e)
{
PasswordBox pBox = sender as PasswordBox;
if (pBox.IsEnabled == false)
{
RoutedEventArgs eventArgs = new RoutedEventArgs(PasswordBoxAttachedEvent.HasBeenDisabledEvent);
pBox.RaiseEvent(eventArgs);
}
if (pBox.IsEnabled == true)
{
RoutedEventArgs eventArgs = new RoutedEventArgs(PasswordBoxAttachedEvent.HasBeenEnabledEvent);
pBox.RaiseEvent(eventArgs);
}
}
}
In the constructor of the View, we define our ViewModel - and set it as DataContext for our View.
Below are some event handlers, that allow us to attach events and properties - but we will talk a bit later about that.
Important: When you go ahead and start learning about MVVM on your own, you might see people saying "In good MVVM there should be no code-behind in View". That is actually complete bull**it :)
It is absolutely fine to put code-behind in View, as long as it doesn't violate any MVVM principles - and often times, it makes it a little bit easier to code some things.
The ViewModel:
namespace PasswordBoxMVVM
{
public class PasswordViewModel : ViewModelBase
{
private bool isPasswordFieldEmpty;
public bool IsPasswordFieldEmpty
{
get { return isPasswordFieldEmpty; }
set
{
isPasswordFieldEmpty = value;
RaisePropertyChanged();
}
}
private SecureString password;
public SecureString Password
{
get { return password; }
set
{
password = value;
RaisePropertyChanged();
}
}
private bool isPassWordFieldDisabled;
public bool IsPasswordFieldDisabled
{
get { return isPassWordFieldDisabled; }
set
{
isPassWordFieldDisabled = value;
RaisePropertyChanged();
}
}
public ICommand ClickCommand { get { return new RelayCommand(doAction, canDoAction); } }
public ICommand PasswordChangedCommand { get { return new RelayCommand(updatePassword, canUpdatePassword); } }
public PasswordViewModel()
{
// Init conditions, need them to not get null reference at the start.
isPassWordFieldDisabled = true;
IsPasswordFieldEmpty = true;
}
private void doAction()
{
IsPasswordFieldDisabled = !IsPasswordFieldDisabled;
}
private bool canDoAction()
{
// Replace this with any condition that you need.
return true;
}
private void updatePassword()
{
if (Password != null)
{
if (Password.Length > 0)
{
isPasswordFieldEmpty = false;
}
else
{
isPasswordFieldEmpty = true;
}
}
else
{
isPasswordFieldEmpty = true;
}
}
private bool canUpdatePassword()
{
// Replace this with any condition that you need.
return true;
}
}
}
There is a lot of things going on in here. First off, we have some public properties, like Password, isPasswordFieldDisabled etc. These are the ones that our View can bind to, and it lets us control the View from our ViewModel.
We also have commands, which are a way of the View to interact with our ViewModel. The View binds certain things to these commands (like events) and we then execute code based on that in our ViewModel.
On this example, a button click fires a command to our ViewModel, which changes the isPasswordBoxDisabled property, that our PasswordBox isEnabled property is bound to - in effect, enabling/disabling the PasswordBox, without any DIRECT interaction between View and ViewModel! Cool, huh?
Converter
namespace PasswordBoxMVVM
{
class PasswordLengthToColorConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
int length = (int)value;
Color output = Colors.Red;
if (length >= 0 && length < 5)
output = Colors.Red;
else if (length >= 5 && length < 6)
output = Colors.Orange;
else if (length >= 6 && length < 7)
output = Colors.Yellow;
else if (length >= 7 && length < 8)
output = Colors.LightGreen;
else if (length >= 8)
output = Colors.Green;
return output;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotSupportedException();
}
}
}
The converter is much like a translator. Here, we translate our Password length into colors - so that we can later use them in animation.
This is a perfect example of what I meant when I said MVVM was modular - you want different convertion? Just slap a binding to a new converter and you are done, no need to rewrite your View!
Password Attached property - look linked project
This is here solely for our need to bind our PasswordBox's password to our ViewModel. Originally, a PasswordBox doesn't support that. Enter attached properties! It allows as to "extend" the possibilites of our PasswordBox, by attaching (hence the name) a new property to it - a one we can bind to our ViewModel.
Same goes for attached events - you can find everything else in the code, as the character quota limits me from pasting more.

wpf fade in animation working just one time

I have created a fade in and out animation, triggered by property changes in the viewmodel. This is working fine when fading in and out the first time, but each time I repeat this only the fade out is displayed, meaning the control remains invisible until it flashes to opacity 1 and then fades out.
XAML:
<DataTemplate x:Key="MessageTemplate">
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding FadeInAnimationState}" Value="Active">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="Opacity">
<EasingDoubleKeyFrame KeyTime="0" Value="0"/>
<EasingDoubleKeyFrame KeyTime="0:0:0.25" Value="1"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
</DataTrigger>
<DataTrigger Binding="{Binding FadeOutAnimationState}" Value="Active">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="Opacity">
<EasingDoubleKeyFrame KeyTime="0" Value="1"/>
<EasingDoubleKeyFrame KeyTime="0:0:1" Value="0"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
</DataTrigger>
</DataTemplate.Triggers>
<StatusBarItem>
...
</StatusBarItem>
</DataTemplate>
...
<ContentPresenter ContentTemplate="{StaticResource MessageTemplate}" Content="{Binding}"/>
The Viewmodel properties responsible for trigggering the animation:
private DisplayState _messageState;
private DisplayState MessageState
{
get { return _messageState; }
set
{
_messageState = value;
if (value == DisplayState.Displayed)
{
FadeOutAnimationState = AnimationState.Inactive;
FadeInAnimationState = AnimationState.Active;
}
else
{
FadeInAnimationState = AnimationState.Inactive;
FadeOutAnimationState = AnimationState.Active;
}
}
}
public AnimationState FadeInAnimationState
{
... // getter and setter with NotfiyPropertyChanged
}
public AnimationState FadeOutAnimationState
{
... // getter and setter with NotfiyPropertyChanged
}
And the call (debug):
MessageState = DisplayState.Displayed;
await Task.Delay(duration);
MessageState = DisplayState.Hidden;
await Task.Delay(TimeSpan.FromSeconds(1));
What am I doing wong?
You need to remove your storyboards after you are done with them. Something like:
<DataTrigger.EnterActions>
<RemoveStoryboard BeginStoryboardName="FadeIn" />
<RemoveStoryboard BeginStoryboardName="FadeOut" />
<BeginStoryboard Name="FadeIn">
<Storyboard>
...
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
(and same for FadeOut, give it a name)
Or, you can do it on the ExitActions:
<DataTrigger.EnterActions>
<BeginStoryboard Name="FadeIn">
<Storyboard>
...
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<RemoveStoryboard BeginStoryboardName="FadeIn" />
</DataTrigger.ExitActions>
Otherwise the last storyboard will keep "pushing" its last value so you won't see any changes

DataBinding with datatriggers XAML with Boolean Local Variable

I am having trouble getting my tabitem to flash when the value for newCall is true. I think i have the Xaml correct but i am not sure how to bind it behind in code. When the variable new Call is set to true i would like my tabitem to flash.
<TabItem.Style>
<Style TargetType="{x:Type TabItem}" BasedOn="{StaticResource MetroTabItem}">
<Style.Resources>
<Storyboard x:Key="flashAnimation" >
<DoubleAnimation Storyboard.TargetProperty="Opacity" From="1" To="0" AutoReverse="True" Duration="0:0:0.5" RepeatBehavior="Forever" />
</Storyboard>
</Style.Resources>
<Style.Triggers>
<DataTrigger Binding="{Binding newCall}" Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard Name="flash" Storyboard="{StaticResource flashAnimation}" />
</DataTrigger.EnterActions>
</DataTrigger>
</Style.Triggers>
</Style>
</TabItem.Style>
Your ViewModel has to implement INotifyPropertyChanged. In the ViewModel add the following Code:
private bool _newCall;
public bool newCall
{
get { return _newCall; }
set
{
_newCall = value;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("newCall"));
}
}
}
and change Binding="{Binding newCall} to Binding="{Binding newCall, UpdateSourceTrigger=PropertyChanged}"
Then, the TabItem will start flashing as soon as newCall is set to true

Error triggering animation based on boolean value

Have a button that gets animated when a IsBusy boolean property on the ViewModel is True:
<Button x:Name="button" Grid.Row="4"
Command="{Binding QuitCommand}"
Content="{x:Static r:Resources.Close}"
RenderTransformOrigin="0.5,0.5">
<Button.Style>
<Style TargetType="Button">
<Style.Triggers>
<DataTrigger Binding="{Binding IsBusy}" Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard Storyboard="{StaticResource TestStoryboard}"/>
</DataTrigger.EnterActions>
</DataTrigger> **<-- here is line 167 position 27**
</Style.Triggers>
</Style>
</Button.Style>
<Button.RenderTransform>
<TransformGroup>
<ScaleTransform/>
<SkewTransform/>
<RotateTransform/>
<TranslateTransform/>
</TransformGroup>
</Button.RenderTransform>
</Button>
Storyboard:
<Window.Resources>
<Storyboard x:Key="TestStoryboard">
<DoubleAnimationUsingKeyFrames
Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[2].(RotateTransform.Angle)">
<EasingDoubleKeyFrame KeyTime="0:0:0.5" Value="25"/>
<EasingDoubleKeyFrame KeyTime="0:0:1" Value="180"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</Window.Resources>
If I initialize the IsBusy property to false and then launch the animation later, then everything works.
If I initialize the IsBusy property to true then I get the following errors:
'[Unknown]' property does not point to a DependencyObject in path
'(0).(1)[2].(2)'. 'Set property
'System.Windows.FrameworkElement.Style' threw an exception.' Line
number '167' and line position '27'.
ViewModel:
public const string IsBusyPropertyName = "IsBusy";
private bool _IsBusy = true;
public bool IsBusy
{
get
{
return _IsBusy;
}
set
{
if (_IsBusy == value)
{
return;
}
RaisePropertyChanging(IsBusyPropertyName);
_IsBusy = value;
RaisePropertyChanged(IsBusyPropertyName);
}
}
Timing issue?
Set
<Button.RenderTransform>
<TransformGroup>
<ScaleTransform/>
<SkewTransform/>
<RotateTransform/>
<TranslateTransform/>
</TransformGroup>
</Button.RenderTransform>
before
<Button.Style>
<Style TargetType="Button">
<Style.Triggers>
<DataTrigger Binding="{Binding IsBusy}" Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard Storyboard="{StaticResource TestStoryboard}"/>
</DataTrigger.EnterActions>
</DataTrigger> **<-- here is line 167 position 27**
</Style.Triggers>
</Style>
</Button.Style>
If it's already true then it calls storyboard before RenderTransform is set, I believe.

C# WPF pausing an animated window with storyboard

I am making a C# wpf application with Visual Studio 2012. Here is the toast like notification. It is animated to go off by 4 seconds. It I get the mouse over this I want to pause the animation.
How can I achieve that ?
<Window x:Class="Exmaple.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:ed="http://schemas.microsoft.com/expression/2010/drawing"
Title="Notification Popup" Width="300" SizeToContent="Height"
WindowStyle="None" AllowsTransparency="True" Height="Auto" Background="Transparent">
<Grid x:Name="abc" RenderTransformOrigin="0,1" Height="Auto" Width="300" Margin="0,0,0,0" MouseEnter="Grid_MouseEnter_1" >
<!-- Notification area -->
<Border BorderThickness="1" Background="#FF2D2D30" BorderBrush="Black" CornerRadius="0" Margin="0,0,0,0">
<!--StackPanel Margin="20"-->
<TextBlock x:Name="textblocknotify" TextWrapping="Wrap" Height="Auto" Margin="5" Foreground="White">
</TextBlock>
<!--CheckBox Content="Checkable" Margin="5 5 0 5" /-->
<!--Button Content="Clickable" HorizontalAlignment="Center" /-->
<!--/StackPanel-->
</Border>
<!-- Animation -->
<Grid.Triggers >
<EventTrigger RoutedEvent="FrameworkElement.Loaded">
<BeginStoryboard>
<Storyboard Completed="Storyboard_Completed_1">
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)">
<SplineDoubleKeyFrame KeyTime="0:0:0" Value="0"/>
<SplineDoubleKeyFrame KeyTime="0:0:0.1" Value="1"/>
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)">
<SplineDoubleKeyFrame KeyTime="0:0:4.5" Value="1"/>
<SplineDoubleKeyFrame KeyTime="0:0:5" Value="0"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="FrameworkElement.Loaded">
<BeginStoryboard>
<Storyboard Completed="Storyboard_Completed_1">
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)">
<SplineDoubleKeyFrame KeyTime="0:0:0" Value="0"/>
<SplineDoubleKeyFrame KeyTime="0:0:0.1" Value="1"/>
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)">
<SplineDoubleKeyFrame KeyTime="0:0:4.5" Value="1"/>
<SplineDoubleKeyFrame KeyTime="0:0:5" Value="0"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Grid.Triggers>
<Grid.RenderTransform>
<ScaleTransform ScaleY="1" />
</Grid.RenderTransform>
</Grid>
Code behind this
public partial class Window1 : Window
{
public Window1(String s)
{
InitializeComponent();
textblocknotify.Text = s;
Dispatcher.BeginInvoke(DispatcherPriority.ApplicationIdle, new Action(() =>
{
var workingArea = System.Windows.SystemParameters.WorkArea;
var transform = PresentationSource.FromVisual(this).CompositionTarget.TransformFromDevice;
var corner = transform.Transform(new Point(workingArea.Right, workingArea.Bottom));
this.Left = corner.X - this.ActualWidth - 10;
this.Top = corner.Y - this.ActualHeight-30;
}));
//this.Close();
}
private void Storyboard_Completed_1(object sender, EventArgs e)
{
this.Close();
}
private void Grid_MouseEnter_1(object sender, MouseEventArgs e)
{
//don't know what to do
}
You can use the PauseStoryboard Class and the UIElement.MouseEnter Event to pause a running Animation. Equally, if you want the Animation to resume when the mouse is no longer over the control, then you can use the ResumeStoryboard Class and the UIElement.MouseLeave Event. Here is a simple example to demonstrate:
<Button Content="Click Me">
<Button.Triggers>
<EventTrigger RoutedEvent="FrameworkElement.Loaded">
<BeginStoryboard Name="OpacityStoryboard">
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="(UIElement.Opacity)"
From="0" To="1" RepeatBehavior="Forever" AutoReverse="True" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="UIElement.MouseEnter">
<PauseStoryboard BeginStoryboardName="OpacityStoryboard" />
</EventTrigger>
<EventTrigger RoutedEvent="UIElement.MouseLeave">
<ResumeStoryboard BeginStoryboardName="OpacityStoryboard" />
</EventTrigger>
</Button.Triggers>
</Button>

Categories