How do I fade text out, change it, and fade it back in?
Asked Answered
D

1

9

I'm using a TextBlock in a datatemplate for a cell in a datagrid. I have a requirement that says when the value of the cell changes, the text should:

  • fade out before changing
  • value should change
  • fade back in again

At the moment I use the TargetUpdated RoutedEvent to trigger an animation to make the text fade away and then come back. But the fade happens after the text has already changed value on screen.

<DataTemplate>
    <Border>
        <TextBlock Name="templateTextBlock" Text="{Binding Path=FirstName, NotifyOnTargetUpdated=True}" />
    </Border>
    <DataTemplate.Triggers>
        <EventTrigger RoutedEvent="Binding.TargetUpdated">
            <BeginStoryboard>
                <Storyboard AutoReverse="True">
                    <DoubleAnimation Storyboard.TargetName="templateTextBlock" Storyboard.TargetProperty="Opacity" To=".1" Duration="0:0:.5" />
                </Storyboard>
            </BeginStoryboard>
        </EventTrigger>
    </DataTemplate.Triggers>
</DataTemplate>

My question is how do I achieve the effect required - fade out, change text, fade in?

Many thanks.

Deductible answered 2/6, 2011 at 23:56 Comment(1)
Silly requirements for the win.Apt
A
12

Wrote an interactivity behavior which should do this:

xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
<TextBlock Text="{Binding Name, NotifyOnTargetUpdated=True}">
    <i:Interaction.Behaviors>
        <b:AnimatedTextChangeBehavior AnimationDuration="0:0:0.1" />
    </i:Interaction.Behaviors>
</TextBlock>
class AnimatedTextChangeBehavior : Behavior<TextBlock>
{
    public Duration AnimationDuration { get; set; }

    private string OldValue = null;
    private string NewValue = null;

    DoubleAnimation AnimationOut;
    DoubleAnimation AnimationIn;

    protected override void OnAttached()
    {
        base.OnAttached();

        AnimationOut = new DoubleAnimation(1, 0, AnimationDuration, FillBehavior.HoldEnd);
        AnimationIn = new DoubleAnimation(0, 1, AnimationDuration, FillBehavior.HoldEnd);
        AnimationOut.Completed += (sOut, eOut) =>
        {
            AssociatedObject.SetCurrentValue(TextBlock.TextProperty, NewValue);
            OldValue = NewValue;
            AssociatedObject.BeginAnimation(TextBlock.OpacityProperty, AnimationIn);
        };

        Binding.AddTargetUpdatedHandler(AssociatedObject, new EventHandler<DataTransferEventArgs>(Updated));
    }

    private void Updated(object sender, DataTransferEventArgs e)
    {
        string value = AssociatedObject.GetValue(TextBlock.TextProperty) as string;
        AssociatedObject.BeginAnimation(TextBlock.OpacityProperty, AnimationOut);
        NewValue =  value;
        if (OldValue == null)
        {
            OldValue = value;
        }
        AssociatedObject.SetCurrentValue(TextBlock.TextProperty, OldValue);
    }
}

If you do not want to use the Blend SDK's Interactivity for this you can just take the code and refactor it into a seperate class and use the TextBlock's Loaded event to do the setup.

Apt answered 3/6, 2011 at 0:56 Comment(4)
I am really glad I found this post. I was using 2 textblocks and animations to do this. This is much cleaner.Dews
@H.B. May I know what name space reference for <b: ?Skim
@abccba: That references the namespace of the class i define in the snippet below. So something like clr-namespace:YourProject.YourNamespace, depends on where you put it.Apt
it's worth pointing out that the Text Binding's NotifyOnSourceUpdated=True must be present otherwise this behaviour won't work (it caught me out anyway!).Kalmuck

© 2022 - 2024 — McMap. All rights reserved.