Search code examples
c#wpfmvvm

Why the trigger is not fired?


I have a TextBlock in my XAML that's configured with a trigger to activate when the user enters incorrect credentials. Here's the XAML snippet:

<TextBlock Text="{Binding ErrorMessage}" HorizontalAlignment="Center" FontSize="12" FontWeight="Medium"
           FontFamily="Montserrat" Foreground="Red" Margin="0,15,0,0">
    <TextBlock.Style>
        <Style TargetType="TextBlock">
            <Style.Triggers>
                <DataTrigger Binding="{Binding ShowErrorAnimation,Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Value="True">
                    <DataTrigger.EnterActions>
                        <BeginStoryboard>
                            <Storyboard>
                                <DoubleAnimation Storyboard.TargetProperty="RenderTransform.(TranslateTransform.X)" From="0" To="10" Duration="0:0:5" AutoReverse="True" RepeatBehavior="3x"/>
                            </Storyboard>
                        </BeginStoryboard>
                    </DataTrigger.EnterActions>
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </TextBlock.Style>
</TextBlock>

In this XAML code, the DataTrigger is bound to the ShowErrorAnimation property from the LoginViewModel. When this property is set to true, the trigger should activate, and the associated animation should run. Here's the relevant code from the LoginViewModel:

public class LoginViewModel: ViewModelBase {

    // Other ViewModel code...

    private String _errorMessage;
    private bool _showErrorAnimation;
    public string ErrorMessage {
      get => _errorMessage;
      set {
        _errorMessage = value;
        onPropertyChanged(nameof(ErrorMessage));
      }
    }
    public bool ShowErrorAnimation {
      get => _showErrorAnimation;
      set {
        _showErrorAnimation = value;
        onPropertyChanged(nameof(ShowErrorAnimation));
      }
    }

    public LoginViewModel() {
      userRepository = new UserRepository();

      LoginCommand = new ViewModelCommand(ExecuteLoginCommand, CanExecuteLoginCommand);
      RecoverPasswordCommand = new ViewModelCommand(p => ExecuteRecoverPasswordCommand("", ""));
      ShowPasswordCommand = new ViewModelCommand(ExecuteShowPasswordCommand, CanExecuteShowPasswordCommand);
      RememberPasswordCommand = new ViewModelCommand(ExecuteRememberPasswordCommand, CanExecuteRememberPasswordCommand);
    }

    private void ExecuteLoginCommand(object obj) {
      bool isUserValid = userRepository.AuthenticateUser(new NetworkCredential(Username, Password));

      if (isUserValid) {
        Thread.CurrentPrincipal = new GenericPrincipal(new GenericIdentity(Username), null);
        IsViewVisible = false;
      } else {
        ErrorMessage = "Invalid user name or passwords";
        _showErrorAnimation = true;
      }

      // Other ViewModel code...
    }

I'm interested in understanding the reason behind the trigger not being activated, which is resulting in the absence of the shaking animation for the text.


Solution

  • This is most likely due to the fact that you did not set an instance of TranslateTransform to the RenderTransform property.
    Demo example:

        <StackPanel DataContext="{Binding ElementName=cBox}">
            <StackPanel.Resources>
                <Style TargetType="TextBlock">
                    <Style.Triggers>
                        <DataTrigger Binding="{Binding IsChecked}"
                                        Value="True">
                            <DataTrigger.EnterActions>
                                <BeginStoryboard>
                                    <Storyboard>
                                        <DoubleAnimation Storyboard.TargetProperty="RenderTransform.(TranslateTransform.X)"
                                                            From="0" To="10" Duration="0:0:5"
                                                            AutoReverse="True" RepeatBehavior="3x"/>
                                    </Storyboard>
                                </BeginStoryboard>
                            </DataTrigger.EnterActions>
                        </DataTrigger>
                    </Style.Triggers>
                </Style>
            </StackPanel.Resources>
            <CheckBox x:Name="cBox" Content="Click Me"/>
            <TextBlock Text="Some Text" HorizontalAlignment="Center" FontSize="12" FontWeight="Medium"
                        FontFamily="Montserrat" Foreground="Red" Margin="0,15,0,0"/>
            <TextBlock Text="Some Text" HorizontalAlignment="Center" FontSize="12" FontWeight="Medium"
                        FontFamily="Montserrat" Foreground="Red" Margin="0,15,0,0" RenderTransformOrigin="0.5,0.5">
                <TextBlock.RenderTransform>
                    <TranslateTransform/>
                </TextBlock.RenderTransform>
            </TextBlock>
    
        </StackPanel>