PropertyInfo Control in CaliburnMicro

PropertyInfo Control in CaliburnMicro

Old forum URL: forums.lhotka.net/forums/t/11633.aspx


blc303 posted on Monday, October 08, 2012

Does anyone have an example of a Style for a TextBox using the PropertyInfo Control with CaliburnMicro?

 

I have been trying to cobble something together based on the WP example but have had absolutely no success.

 

I get the impression that the DataTriggers aren't firing but I have no idea what I am doing wrong. I suspect the Binding is completely wrong.

Here is my relatively primitive start.

            <LinearGradientBrush x:Key="TextBoxBorder" EndPoint="0,20" MappingMode="Absolute" StartPoint="0,0">
                <GradientStop Color="#ABADB3" Offset="0.05"/>
                <GradientStop Color="#E2E3EA" Offset="0.07"/>
                <GradientStop Color="#E3E9EF" Offset="1"/>
            </LinearGradientBrush>
            
            <SolidColorBrush x:Key="ValidatonBorderBrushWarning" Color="#F0F0CA00"/>
            <SolidColorBrush x:Key="ValidatonBorderBrushError" Color="#FFC1121C"/>
            <SolidColorBrush x:Key="ValidatonBorderBrushInfo" Color="#FF1174EC"/>
            <CornerRadius x:Key="TextBoxCornerRadius">5</CornerRadius>
            <SolidColorBrush x:Key="DisabledForegroundBrush" Color="#888" />
            <SolidColorBrush x:Key="DisabledBackgroundBrush" Color="#EEE" />
            <SolidColorBrush x:Key="WindowBackgroundBrush" Color="#FFF" />
            <SolidColorBrush x:Key="SelectedBackgroundBrush" Color="#DDD" />

            <Style x:Key="CSLATextBox" BasedOn="{x:Null}" TargetType="{x:Type TextBox}">
                <Setter Property="SnapsToDevicePixels" Value="True"/>
                <Setter Property="OverridesDefaultStyle" Value="True"/>
                <Setter Property="KeyboardNavigation.TabNavigation" Value="None"/>
                <Setter Property="FocusVisualStyle" Value="{x:Null}"/>
                <Setter Property="MinWidth" Value="120"/>
                <Setter Property="MinHeight" Value="20"/>
                <Setter Property="AllowDrop" Value="true"/>

                <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
                <Setter Property="Background" Value="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"/>
                <Setter Property="Template">
                    <Setter.Value>
                        <ControlTemplate TargetType="{x:Type TextBox}">
                            <Grid>
                                <Border x:Name="ValidationElement" 
                                    CornerRadius="{StaticResource TextBoxCornerRadius}" 
                                    Padding="2"     
                                    BorderThickness="2"
                                    BorderBrush="{StaticResource ValidatonBorderBrushError}"
                                    Visibility="Collapsed">
                                    <Grid HorizontalAlignment="Right" 
                                      VerticalAlignment="Top" 
                                      Margin="0,-4,-4,0"
                                      Height="14" 
                                      Width="14">
                                        <Path Data="M0,0 L15,0 14,14" 
                                          Fill="{StaticResource ValidatonBorderBrushError}" />
                                        <Path Margin="0,0,0,0" Data="M 0,0 L2,0 L 14,12 L14,14" Fill="#ffffff"/>                                        
                                    </Grid>
                                </Border>

                                <Border Name="Border"
                                    CornerRadius="{StaticResource TextBoxCornerRadius}" 
                                    Padding="2"     
                                    BorderThickness="1"                            
                                    BorderBrush="{StaticResource TextBoxBorder}"
                                    Visibility="Visible" >
                                </Border>
                                
                                <StackPanel Orientation="Horizontal">
                                    <ScrollViewer Margin="2" x:Name="PART_ContentHost" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>                                    
                                </StackPanel>
                                
                                <csla:PropertyInfo Property="{Binding Text, RelativeSource={RelativeSource TemplatedParent}}" x:Name="pi">
                                    <i:Interaction.Triggers>
                                        <ei:DataTrigger Binding="{Binding Path=RuleSeverity, ElementName=pi}">
                                            <ei:DataTrigger.Value>
                                                <cslaRules:RuleSeverity>Error</cslaRules:RuleSeverity>
                                            </ei:DataTrigger.Value>
                                            <ei:ChangePropertyAction 
                                            TargetName="ValidationElement" 
                                            PropertyName="BorderBrush"
                                            Value="{StaticResource ValidatonBorderBrushError}"/>
                                            <ei:ChangePropertyAction 
                                            TargetName="ValidationElement" 
                                            PropertyName="Visibility"
                                            Value="Visible"/>
                                            <ei:ChangePropertyAction 
                                            TargetName="Border" 
                                            PropertyName="Visibility"
                                            Value="Collapsed"/>                                            
                                        </ei:DataTrigger>
                                        <ei:DataTrigger Binding="{Binding Path=RuleSeverity, ElementName=pi}">
                                            <ei:DataTrigger.Value>
                                                <cslaRules:RuleSeverity>Warning</cslaRules:RuleSeverity>
                                            </ei:DataTrigger.Value>
                                            <ei:ChangePropertyAction 
                                            TargetName="ValidationElement" 
                                            PropertyName="BorderBrush"
                                            Value="{StaticResource ValidatonBorderBrushWarning}"/>
                                            <ei:ChangePropertyAction 
                                            TargetName="ValidationElement" 
                                            PropertyName="Visibility"
                                            Value="Visible"/>
                                            <ei:ChangePropertyAction 
                                            TargetName="Border" 
                                            PropertyName="Visibility"
                                            Value="Collapsed"/>
                                        </ei:DataTrigger>
                                        <ei:DataTrigger Binding="{Binding Path=RuleSeverity, ElementName=pi}">
                                            <ei:DataTrigger.Value>
                                                <cslaRules:RuleSeverity>Information</cslaRules:RuleSeverity>
                                            </ei:DataTrigger.Value>
                                            <ei:ChangePropertyAction 
                                            TargetName="ValidationElement" 
                                            PropertyName="BorderBrush"
                                            Value="{StaticResource ValidatonBorderBrushInfo}"/>
                                            <ei:ChangePropertyAction 
                                            TargetName="ValidationElement" 
                                            PropertyName="Visibility"
                                            Value="Visible"/>
                                            <ei:ChangePropertyAction 
                                            TargetName="Border" 
                                            PropertyName="Visibility"
                                            Value="Collapsed"/>
                                        </ei:DataTrigger>
                                        <ei:DataTrigger Binding="{Binding Path=RuleSeverity, ElementName=pi}">
                                            <ei:DataTrigger.Value>
                                                <cslaRules:RuleSeverity>Information</cslaRules:RuleSeverity>
                                            </ei:DataTrigger.Value>
                                            <ei:ChangePropertyAction 
                                            TargetName="ValidationElement" 
                                            PropertyName="BorderBrush"
                                            Value="{StaticResource ValidatonBorderBrushInfo}"/>
                                            <ei:ChangePropertyAction 
                                            TargetName="ValidationElement" 
                                            PropertyName="Visibility"
                                            Value="Visible"/>
                                            <ei:ChangePropertyAction 
                                            TargetName="Border" 
                                            PropertyName="Visibility"
                                            Value="Collapsed"/>
                                        </ei:DataTrigger>

                                        <ei:DataTrigger Binding="{Binding CanRead, ElementName=pi}" Value="False">
                                            <ei:ChangePropertyAction TargetObject="{Binding RelativeSource={RelativeSource TemplatedParent}}" 
                                                       PropertyName="Visibility" Value="Collapsed" />
                                        </ei:DataTrigger>
                                        <ei:DataTrigger Binding="{Binding CanWrite, ElementName=pi}" Value="False">
                                            <ei:ChangePropertyAction TargetObject="{Binding RelativeSource={RelativeSource TemplatedParent}}" 
                                                       PropertyName="IsReadOnly" Value="True" />
                                        </ei:DataTrigger>
                                    </i:Interaction.Triggers>
                                </csla:PropertyInfo>
                            </Grid>

                            <ControlTemplate.Triggers>
                                <Trigger Property="IsEnabled" Value="False">
                                    <Setter TargetName="Border" Property="Background" Value="{DynamicResource {x:Static SystemColors.ControlBrushKey}}"/>
                                    <Setter TargetName="Border" Property="BorderBrush" Value="{StaticResource DisabledBackgroundBrush}"/>
                                    <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
                                </Trigger>
                            </ControlTemplate.Triggers>
                        </ControlTemplate>
                    </Setter.Value>
                </Setter>
            </Style>

Anyone have any suggestions? Thanks

RockfordLhotka replied on Monday, October 08, 2012

Make sure you are running the latest 4.3 build. There were some bug fixes in PropertyInfo to deal with issues like you are encountering.

anottest replied on Monday, October 08, 2012

I'd be happy to help you out. Can you describe which features of PropertyInfo you want to implement (i.e. - authorization, validation, or both)? Also, it looks like you're using WPF, can you confirm that?

Thanks,
Aaron

blc303 replied on Tuesday, October 09, 2012

@Rocky, I am running Version 4.3.13.0

 

@Aaron

Yes, I am working with WPF.

I my first concern is validation but I am working on a WPF Caliburn / CSLA Framework and would like to eventually include authorization if possible.

What I would love is a dead simple, self-contained  WPF example showing on a text validation (maybe change the background color) and authorization (Collapse or Disable). By self-contained I mean no external color resources, formatting etc. I should be able to adapt from there or perhaps see what I am doing wrong.

My main problem is that with the amount of behind the scenes data binding going on, it's really difficult to actually see what isn't working.

I would be forever grateful if you had something like that.

Thanks

anottest replied on Tuesday, October 09, 2012

Below is a sample WPF TextBox template that incorporates validation, authorization, and property busy status for a CSLA object. This example supports warning and information level errors as well as the normal validation errors. Authorization rules are handled by collapsing the textbox if the user does not have permission to read the property, and marks the textbox as read only if the user does not have write permissions.

Obviously you can tailor the template to your needs, but I hope this gets you a good start.

xmlns:csla="clr-namespace:Csla.Xaml;assembly=Csla.Xaml"
xmlns:cslaRules="clr-namespace:Csla.Rules;assembly=Csla"
xmlns:i=http://schemas.microsoft.com/expression/2010/interactivity
xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"

<SolidColorBrush x:Key="ValidationBrush_Error" Color="#FFDC000C"/>
<SolidColorBrush x:Key="ValidationBrush_Warning" Color="#FFEBF900"/>
<SolidColorBrush x:Key="ValidationBrush_Info" Color="#FF0000FF"/>

<Style x:Key="TextBoxStyle" TargetType="TextBox">
    <Setter Property="Margin" Value="0,1,10,1"/>
    <Setter Property="VerticalAlignment" Value="Stretch"/>
    <Setter Property="FontFamily" Value="Segoe WP"/>
    <Setter Property="Height" Value="30" />
    <Setter Property="MinWidth" Value="200" />
    <Setter Property="FontSize" Value="16" />
    <Setter Property="VerticalContentAlignment" Value="Center" />   
    <Setter Property="BorderBrush" Value="White" />
    <Setter Property="Template">
      <Setter.Value>
        <ControlTemplate TargetType="TextBox">
            <Grid x:Name="RootElement">
              <VisualStateManager.VisualStateGroups>
                <VisualStateGroup x:Name="CommonStates">
                  <VisualState x:Name="Normal"/>
                  <VisualState x:Name="MouseOver">
                    <Storyboard>
                      <ColorAnimation Storyboard.TargetName="MouseOverBorder" Storyboard.TargetProperty="(Border.BorderBrush).(SolidColorBrush.Color)" To="#FF99C1E2" Duration="0"/>
                    </Storyboard>
                  </VisualState>
                  <VisualState x:Name="Disabled">
                    <Storyboard>
                      <DoubleAnimation Storyboard.TargetName="DisabledVisualElement" Storyboard.TargetProperty="Opacity" To="1" Duration="0"/>
                    </Storyboard>
                  </VisualState>
                  <VisualState x:Name="ReadOnly">
                    <Storyboard>
                      <DoubleAnimation Storyboard.TargetName="ReadOnlyVisualElement" Storyboard.TargetProperty="Opacity" To="1" Duration="0" />
                    </Storyboard>
                  </VisualState>
                </VisualStateGroup>
                <VisualStateGroup x:Name="FocusStates">
                  <VisualState x:Name="Focused">
                    <Storyboard>
                      <DoubleAnimation Storyboard.TargetName="FocusVisualElement" Storyboard.TargetProperty="Opacity" To="1" Duration="0"/>
                    </Storyboard>
                  </VisualState>
                  <VisualState x:Name="Unfocused">
                    <Storyboard>
                      <DoubleAnimation Storyboard.TargetName="FocusVisualElement" Storyboard.TargetProperty="Opacity" To="0" Duration="0"/>
                    </Storyboard>
                  </VisualState>
                </VisualStateGroup>
                <VisualStateGroup x:Name="CslaValidationStates">
                  <VisualState x:Name="CslaValid"/>
                  <VisualState x:Name="CslaInvalidUnfocused">
                    <Storyboard>
                      <ObjectAnimationUsingKeyFrames Storyboard.TargetName="ValidationErrorElement" Storyboard.TargetProperty="Visibility">
                        <DiscreteObjectKeyFrame KeyTime="0">
                          <DiscreteObjectKeyFrame.Value>
                            <Visibility>Visible</Visibility>
                          </DiscreteObjectKeyFrame.Value>
                        </DiscreteObjectKeyFrame>
                      </ObjectAnimationUsingKeyFrames>
                    </Storyboard>
                  </VisualState>
                  <VisualState x:Name="CslaInvalidFocused">
                    <Storyboard>
                      <ObjectAnimationUsingKeyFrames Storyboard.TargetName="ValidationErrorElement" Storyboard.TargetProperty="Visibility">
                        <DiscreteObjectKeyFrame KeyTime="0">
                          <DiscreteObjectKeyFrame.Value>
                            <Visibility>Visible</Visibility>
                          </DiscreteObjectKeyFrame.Value>
                        </DiscreteObjectKeyFrame>
                      </ObjectAnimationUsingKeyFrames>
                      <ObjectAnimationUsingKeyFrames Storyboard.TargetName="validationPopup" Storyboard.TargetProperty="IsOpen">
                        <DiscreteObjectKeyFrame KeyTime="0">
                          <DiscreteObjectKeyFrame.Value>
                            <System:Boolean>True</System:Boolean>
                          </DiscreteObjectKeyFrame.Value>
                        </DiscreteObjectKeyFrame>
                      </ObjectAnimationUsingKeyFrames>
                    </Storyboard>
                  </VisualState>
                </VisualStateGroup>
                <VisualStateGroup x:Name="BusyStates">
                  <VisualState x:Name="Idle"/>
                  <VisualState x:Name="Busy">
                    <Storyboard>
                      <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)" Storyboard.TargetName="BusyElement" RepeatBehavior="Forever">
                        <EasingDoubleKeyFrame KeyTime="0" Value="0"/>
                        <EasingDoubleKeyFrame KeyTime="0:0:0.5" Value="1">
                          <EasingDoubleKeyFrame.EasingFunction>
                            <QuinticEase EasingMode="EaseIn"/>
                          </EasingDoubleKeyFrame.EasingFunction>
                        </EasingDoubleKeyFrame>
                        <EasingDoubleKeyFrame KeyTime="0:0:1" Value="0"/>
                      </DoubleAnimationUsingKeyFrames>
                    </Storyboard>
                  </VisualState>
                </VisualStateGroup>
              </VisualStateManager.VisualStateGroups>

              <Border x:Name="Border" BorderThickness="{TemplateBinding BorderThickness}" CornerRadius="0" Opacity="1" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}">
                <Grid>
                  <Border x:Name="ReadOnlyVisualElement" Opacity="0" Background="#5EC9C9C9"/>
                  <Border x:Name="MouseOverBorder" BorderThickness="1" BorderBrush="Transparent">
                    <ScrollViewer x:Name="PART_ContentHost" Padding="{TemplateBinding Padding}" BorderThickness="0" IsTabStop="False"/>
                  </Border>
                </Grid>
              </Border>
              <Border x:Name="DisabledVisualElement" Background="#A5F7F7F7" BorderBrush="#A5F7F7F7" BorderThickness="{TemplateBinding BorderThickness}" Opacity="0" IsHitTestVisible="False"/>
              <Border x:Name="FocusVisualElement" BorderBrush="#FF212B40" BorderThickness="{TemplateBinding BorderThickness}" Margin="0" Opacity="0" IsHitTestVisible="False"/>
              <Border x:Name="ValidationErrorElement" BorderThickness="1" CornerRadius="0" BorderBrush="{StaticResource ValidationBrush_Error}" Visibility="Collapsed">
                <Grid>
                  <Popup x:Name="validationPopup"
                           AllowsTransparency="True"
                           PopupAnimation="None"
                           Placement="Bottom"
                           PlacementTarget="{Binding RelativeSource={RelativeSource TemplatedParent}}"
                           DataContext="{Binding ElementName=pi}"
                           VerticalOffset="-25">
                    <Grid x:Name="Root" Margin="0" Opacity="0">
                      <Border x:Name="validationPopupBorder" Background="{StaticResource ValidationBrush_Error}" CornerRadius="0"/>
                      <Border CornerRadius="0">
                        <TextBlock x:Name="validationPopupText"
                                     UseLayoutRounding="false"
                                     Foreground="White"
                                     FontSize="11"
                                     Margin="8,4,8,4"
                                     MaxWidth="250"
                                     TextWrapping="Wrap"
                                     Text="{Binding Path=RuleDescription}" />
                      </Border>
                    </Grid>
                  </Popup>
                  <Grid Width="12" Height="12" HorizontalAlignment="Right" Margin="1,-4,-4,0" VerticalAlignment="Top" Background="Transparent">
                    <Path Margin="1,3,0,0" Data="M 1,0 L6,0 A 2,2 90 0 1 8,2 L8,7 z" x:Name="ValidationErrorElement_Path" Fill="{StaticResource ValidationBrush_Error}"/>
                    <Path Margin="1,3,0,0" Data="M 0,0 L2,0 L 8,6 L8,8" Fill="#ffffff"/>
                  </Grid>
                </Grid>
              </Border>
              <Rectangle x:Name="BusyElement" Fill="#FF7A7A7A" HorizontalAlignment="Right" Width="12" Height="12" VerticalAlignment="Center" Margin="5,5,10,5" Opacity="0"/>

              <csla:PropertyInfo Property="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Text}" x:Name="pi">
                <i:Interaction.Triggers>
                  <ei:DataTrigger Binding="{Binding Path=RuleSeverity, ElementName=pi}">
                    <ei:DataTrigger.Value>
                      <cslaRules:RuleSeverity>Error</cslaRules:RuleSeverity>
                    </ei:DataTrigger.Value>
                    <ei:ChangePropertyAction TargetName="validationPopupText"
                                             PropertyName="Foreground"
                                             Value="White" />
                    <ei:ChangePropertyAction TargetName="validationPopupBorder"
                                             PropertyName="Background"
                                             Value="{StaticResource ValidationBrush_Error}" />
                    <ei:ChangePropertyAction TargetName="ValidationErrorElement"
                                             PropertyName="BorderBrush"
                                             Value="{StaticResource ValidationBrush_Error}" />
                    <ei:ChangePropertyAction TargetName="ValidationErrorElement_Path"
                                             PropertyName="Fill"
                                             Value="{StaticResource ValidationBrush_Error}" />
                  </ei:DataTrigger>

                  <ei:DataTrigger Binding="{Binding Path=RuleSeverity, ElementName=pi}">
                    <ei:DataTrigger.Value>
                      <cslaRules:RuleSeverity>Warning</cslaRules:RuleSeverity>
                    </ei:DataTrigger.Value>
                    <ei:ChangePropertyAction TargetName="validationPopupText"
                                             PropertyName="Foreground"
                                             Value="Black" />
                    <ei:ChangePropertyAction TargetName="validationPopupBorder"
                                             PropertyName="Background"
                                             Value="{StaticResource ValidationBrush_Warning}" />
                    <ei:ChangePropertyAction TargetName="ValidationErrorElement"
                                             PropertyName="BorderBrush"
                                             Value="{StaticResource ValidationBrush_Warning}" />
                    <ei:ChangePropertyAction TargetName="ValidationErrorElement_Path"
                                             PropertyName="Fill"
                                             Value="{StaticResource ValidationBrush_Warning}" />
                  </ei:DataTrigger>

                  <ei:DataTrigger Binding="{Binding Path=RuleSeverity, ElementName=pi}">
                    <ei:DataTrigger.Value>
                      <cslaRules:RuleSeverity>Information</cslaRules:RuleSeverity>
                    </ei:DataTrigger.Value>
                    <ei:ChangePropertyAction TargetName="validationPopupText"
                                             PropertyName="Foreground"
                                             Value="White" />
                    <ei:ChangePropertyAction TargetName="validationPopupBorder"
                                             PropertyName="Background"
                                             Value="{StaticResource ValidationBrush_Info}" />
                    <ei:ChangePropertyAction TargetName="ValidationErrorElement"
                                             PropertyName="BorderBrush"
                                             Value="{StaticResource ValidationBrush_Info}" />
                    <ei:ChangePropertyAction TargetName="ValidationErrorElement_Path"
                                             PropertyName="Fill"
                                             Value="{StaticResource ValidationBrush_Info}" />
                  </ei:DataTrigger>

                  <i:EventTrigger SourceObject="{Binding RelativeSource={RelativeSource TemplatedParent}}" EventName="GotFocus">
                    <i:Interaction.Behaviors>
                      <ei:ConditionBehavior>
                        <ei:ConditionalExpression>
                          <ei:ComparisonCondition LeftOperand="{Binding IsValid, ElementName=pi}" RightOperand="False"/>
                        </ei:ConditionalExpression>
                      </ei:ConditionBehavior>
                    </i:Interaction.Behaviors>
                    <ei:GoToStateAction StateName="CslaInvalidFocused"/>
                  </i:EventTrigger>
                  <i:EventTrigger SourceObject="{Binding RelativeSource={RelativeSource TemplatedParent}}" EventName="LostFocus">
                    <i:Interaction.Behaviors>
                      <ei:ConditionBehavior>
                        <ei:ConditionalExpression>
                          <ei:ComparisonCondition LeftOperand="{Binding IsValid, ElementName=pi}" RightOperand="False"/>
                        </ei:ConditionalExpression>
                      </ei:ConditionBehavior>
                    </i:Interaction.Behaviors>
                    <ei:GoToStateAction StateName="CslaInvalidUnfocused"/>
                  </i:EventTrigger>
                  <ei:DataTrigger Binding="{Binding IsValid, ElementName=pi}" Value="False">
                    <ei:GoToStateAction StateName="CslaInvalidUnfocused"/>
                  </ei:DataTrigger>
                  <ei:DataTrigger Binding="{Binding IsValid, ElementName=pi}" Value="True">
                    <ei:GoToStateAction StateName="CslaValid"/>
                  </ei:DataTrigger>

                  <ei:DataTrigger Binding="{Binding IsBusy, ElementName=pi}" Value="True">
                    <ei:GoToStateAction StateName="Busy"/>
                  </ei:DataTrigger>
                  <ei:DataTrigger Binding="{Binding IsBusy, ElementName=pi}" Value="False">
                    <ei:GoToStateAction StateName="Idle"/>
                  </ei:DataTrigger>

                  <ei:DataTrigger Binding="{Binding CanRead, ElementName=pi}" Value="False">
                    <ei:ChangePropertyAction TargetObject="{Binding RelativeSource={RelativeSource TemplatedParent}}"
                                               PropertyName="Visibility" Value="Collapsed" />
                  </ei:DataTrigger>
                  <ei:DataTrigger Binding="{Binding CanWrite, ElementName=pi}" Value="False">
                    <ei:ChangePropertyAction TargetObject="{Binding RelativeSource={RelativeSource TemplatedParent}}"
                                               PropertyName="IsReadOnly" Value="True" />
                  </ei:DataTrigger>
                </i:Interaction.Triggers>
              </csla:PropertyInfo>

            </Grid>

            <ControlTemplate.Triggers>
              <DataTrigger Binding="{Binding ElementName=validationPopup, Path=IsOpen}" Value="True">
                <DataTrigger.EnterActions>
                  <BeginStoryboard>
                    <Storyboard>
                      <DoubleAnimation Storyboard.TargetName="validationPopup" Storyboard.TargetProperty="VerticalOffset" To="0" Duration="0:0:0.2">
                        <DoubleAnimation.EasingFunction>
                          <BackEase Amplitude=".3" EasingMode="EaseOut"/>
                        </DoubleAnimation.EasingFunction>
                      </DoubleAnimation>
                      <DoubleAnimation Storyboard.TargetName="Root" Storyboard.TargetProperty="Opacity" To="1" Duration="0:0:0.2"/>
                    </Storyboard>
                  </BeginStoryboard>
                </DataTrigger.EnterActions>
                <DataTrigger.ExitActions>
                  <BeginStoryboard>
                    <Storyboard>
                      <DoubleAnimation Storyboard.TargetName="validationPopup" Storyboard.TargetProperty="VerticalOffset" To="-25" Duration="0:0:0.2">
                        <DoubleAnimation.EasingFunction>
                          <BackEase Amplitude=".3" EasingMode="EaseOut"/>
                        </DoubleAnimation.EasingFunction>
                      </DoubleAnimation>
                      <DoubleAnimation Storyboard.TargetName="Root" Storyboard.TargetProperty="Opacity" To="0" Duration="0:0:0.2"/>
                    </Storyboard>
                  </BeginStoryboard>
                </DataTrigger.ExitActions>
              </DataTrigger>
            </ControlTemplate.Triggers>
          </ControlTemplate>         
      </Setter.Value>
    </Setter>
</Style> 

Copyright (c) Marimer LLC