views:

408

answers:

1

Ok, I have just run into something that is really catching me off-guard.

I was helping a fellow developer with a couple of unrelated questions and in his project he was animating text into some TextBlock(s). So, I went back to my desk and recreated the project (in order to answer his questions), but I accidentally used TextBox instead of TextBlock. My text wasn't animating at all! (A lot of help, I was!)

Eventually, I figured out that his xaml was using TextBlock and mine was using TextBox. What is interesting, is that Blend wasn't creating key frames when I was using TextBox. So, I got it to work in Blend using TextBlock(s) and then modified the xaml by hand, converting the TextBlock(s) into TextBox(es). When I ran the project, I got the following error:

InvalidOperationException: '(0)' Storyboard.TargetProperty path contains nonanimatable property 'Text'.

Well, it seems as if Blend was smart enough to know that ... and not generate the key frames in the animation (it would just modify the value directly on the TextBox). +1 for Blend.

So, the question became: why isn't TextBox.Text animatable? The usual answer is that the particular property you are animating isn't a DependencyProperty. But, this isn't the case, TextBox.Text is a DependencyProperty.

So, now I am bewildered! Why can't you animate TextBox.Text?


Let me include some xaml to illustrate the problem. The following xaml works ... but uses TextBlock(s).

<Window
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    x:Class="TextBoxTextQuestion.MainWindow"
    x:Name="Window"
    Title="MainWindow"
    Width="640"
    Height="480"
>
    <Window.Resources>
        <Storyboard x:Key="animateTextStoryboard">
            <StringAnimationUsingKeyFrames Storyboard.TargetProperty="(TextBlock.Text)" Storyboard.TargetName="textControl">
                <DiscreteStringKeyFrame KeyTime="0:0:1" Value="Goodbye"/>
            </StringAnimationUsingKeyFrames>
        </Storyboard>
    </Window.Resources>
    <Window.Triggers>
        <EventTrigger RoutedEvent="FrameworkElement.Loaded">
            <BeginStoryboard Storyboard="{StaticResource animateTextStoryboard}"/>
        </EventTrigger>
    </Window.Triggers>
    <Grid x:Name="LayoutRoot">
        <StackPanel Orientation="Vertical" HorizontalAlignment="Center" VerticalAlignment="Center">
            <TextBlock x:Name="textControl" Text="Hello" FontFamily="Calibri" FontSize="32"/>
            <TextBlock Text="World!" Margin="0,25,0,0" FontFamily="Calibri" FontSize="32"/>
        </StackPanel>
    </Grid>
</Window>

The following xaml does not work and uses TextBox.Text:

<Window
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    x:Class="TextBoxTextQuestion.MainWindow"
    x:Name="Window"
    Title="MainWindow"
    Width="640"
    Height="480"
>
    <Window.Resources>
        <Storyboard x:Key="animateTextStoryboard">
            <StringAnimationUsingKeyFrames Storyboard.TargetProperty="(TextBox.Text)" Storyboard.TargetName="textControl">
                <DiscreteStringKeyFrame KeyTime="0:0:1" Value="Goodbye"/>
            </StringAnimationUsingKeyFrames>
        </Storyboard>
    </Window.Resources>
    <Window.Triggers>
        <EventTrigger RoutedEvent="FrameworkElement.Loaded">
            <BeginStoryboard Storyboard="{StaticResource animateTextStoryboard}"/>
        </EventTrigger>
    </Window.Triggers>
    <Grid x:Name="LayoutRoot">
        <StackPanel Orientation="Vertical" HorizontalAlignment="Center" VerticalAlignment="Center">
            <TextBox x:Name="textControl" Text="Hello" FontFamily="Calibri" FontSize="32"/>
            <TextBox Text="World!" Margin="0,25,0,0" FontFamily="Calibri" FontSize="32"/>
        </StackPanel>
    </Grid>
</Window>
+10  A: 

Trying to animate the TextBox manually ....

var timeline = new StringAnimationUsingKeyFrames();
timeline.KeyFrames.Add(new DiscreteStringKeyFrame("Goodbye", KeyTime.FromTimeSpan(new TimeSpan(0,0,1))));
textControl.BeginAnimation(TextBox.TextProperty, timeline);

...reveals a more useful error message. The last line fails with the following ArgumentException:

'Text' property is not animatable on 'System.Windows.Controls.TextBox' class because the IsAnimationProhibited flag has been set on the UIPropertyMetadata used to associate the property with the class.
Parameter name: dp

The documentation of UIPropertyMetadata.IsAnimationProhibited says:

In general, the default dependency properties available in the Windows Presentation Foundation (WPF) framework implementation APIs can be animated. You might set this property to true in the metadata of your own custom dependency property to disable animations on it.

Apparently, the designers of the WPF library decided that animating the Text depdendency property of a TextBox is not a good idea and explicitly disabled it.

So, that's the technical answer as to why this property cannot be animated. Why did they disable it? I have no idea...

PS: A quick look at the static constructors of TextBox, TextBoxBase and Control with Reflector reveals that Text is the only TextBox dependency property that cannot be animated.

Heinzi
Great answer! This was driving me nuts.Interestingly enough, I don't see that property metadata set in the static constructor (using Reflector) for TextBox. In fact, I checked for any properties like that before posting the question. I wonder where that property metadata is set for the Text property.
cplotts
The static constructor of `TextBox` uses this overload of the `FrameworkPropertyMetadata` constructor: http://msdn.microsoft.com/en-us/library/ms557303.aspx and passes `true` for the `isAnimationProhibited` argument.
Heinzi
Ah, I missed that. Again, nice job.
cplotts
I've noticed that setting the Text property seems to create quite a few objects whose types indicate that behind the scenes TextBox may be actually creating a full document. My guess is they decided to prevent animation of the Text property because they would have to recreate the document every time the Text changed and then have to deal with updating TextPointers to the new document, etc. However there is nothing preventing you from using a Binding to bind the Text property of a TextBox to an locally attached string property and animating that. This works quite well.
Ray Burns