views:

125

answers:

2

I would like to have an item's width shrink on a click of a button.

Right now I have two objects basically, when you click the button on objectA, a storyboard starts that rotates it around the x-axis and collapses it. Then it shows objectB by setting it's visibility to visible and rotates it around into view.

All I want to add is setting the width smaller while the storyboard is happening to objectA and objectB and then setting it back to normal at the end of the storyboard.

I tried setting the Thickness but I got a compile-time error complaining that it was readonly.

<ObjectAnimationUsingKeyFrames
            BeginTime="00:00:00"
            Storyboard.TargetName="objectA"
            Storyboard.TargetProperty="(UIElement.Margin)">
      <DiscreteObjectKeyFrame KeyTime="00:00:00">
         <DiscreteObjectKeyFrame.Value>
            <Thickness Left="10" Right="10"/>
         </DiscreteObjectKeyFrame.Value>
      </DiscreteObjectKeyFrame>
   </ObjectAnimationUsingKeyFrames>

I have a simple layout for now...

Here is my UI XAML:

<StackPanel>
   <Border x:Name="objectA" BorderBrush="Blue" BorderThickness="1" Height="100" Width="100">
      <StackPanel>
         <TextBox Margin="10"></TextBox>
         <Button Width="50" x:Name="btn1" Content="Flip" Click="btn1_Click"/>
      </StackPanel>
    <Border.Projection>
      <PlaneProjection RotationX="0"></PlaneProjection>
    </Border.Projection>
  </Border>

  <Border Visibility="Collapsed" x:Name="objectB" BorderBrush="Red" BorderThickness="1" Height="100" Width="100">
     <StackPanel>
        <TextBox Margin="10"></TextBox>
        <Button Width="50" x:Name="btn2"  Content="Flip" Click="btn2_Click"/>
     </StackPanel>
     <Border.Projection>
        <PlaneProjection RotationX="90"></PlaneProjection>
     </Border.Projection>
  </Border>

Here is the storyboard...

 <Storyboard x:Name="Storyboardtest">
            <DoubleAnimation BeginTime="00:00:00"
              Storyboard.TargetName="objectA"
              Storyboard.TargetProperty="(UIElement.Projection).(RotationX)"

              From="0" To="-90">
            </DoubleAnimation>
            <ObjectAnimationUsingKeyFrames
                BeginTime="00:00:01"
                Storyboard.TargetName="objectA"
                Storyboard.TargetProperty="(UIElement.Visibility)">

                <DiscreteObjectKeyFrame KeyTime="00:00:00">
                    <DiscreteObjectKeyFrame.Value>
                        <Visibility>Collapsed</Visibility>
                    </DiscreteObjectKeyFrame.Value>
                </DiscreteObjectKeyFrame>

            </ObjectAnimationUsingKeyFrames>

            <ObjectAnimationUsingKeyFrames
                BeginTime="00:00:01"
                Storyboard.TargetName="objectB"
                Storyboard.TargetProperty="(UIElement.Visibility)">

                <DiscreteObjectKeyFrame KeyTime="00:00:00">
                    <DiscreteObjectKeyFrame.Value>
                        <Visibility>Visible</Visibility>
                    </DiscreteObjectKeyFrame.Value>
                </DiscreteObjectKeyFrame>

            </ObjectAnimationUsingKeyFrames>

            <DoubleAnimation BeginTime="00:00:01"
              Storyboard.TargetName="objectB"
              Storyboard.TargetProperty="(UIElement.Projection).(RotationX)"

              From="90" To="0">
            </DoubleAnimation>

        </Storyboard>
+3  A: 

If it is just the visual width you want to affect, add the following to your storyboard. It will give the appearance of the controls moving into the distance and back as it flips:

    <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(CompositeTransform.ScaleX)" Storyboard.TargetName="objectA">
        <EasingDoubleKeyFrame KeyTime="0" Value="1"/>
        <EasingDoubleKeyFrame KeyTime="0:0:1" Value="0.5"/>
    </DoubleAnimationUsingKeyFrames>
    <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(CompositeTransform.ScaleX)" Storyboard.TargetName="objectB">
        <EasingDoubleKeyFrame KeyTime="0" Value="1"/>
        <EasingDoubleKeyFrame KeyTime="0:0:1" Value="0.5"/>
        <EasingDoubleKeyFrame KeyTime="0:0:2" Value="1"/>
    </DoubleAnimationUsingKeyFrames>

you will also need to add the following as I used Expression blend to add the animation and it adds any required elements automatically:

    <Border x:Name="objectA" BorderBrush="Blue" BorderThickness="1" Height="100" Width="100" RenderTransformOrigin="0.5,0.5">
        <Border.RenderTransform>
            <CompositeTransform/>
        </Border.RenderTransform>

[Snip]

    <Border Visibility="Collapsed" x:Name="objectB" BorderBrush="Red" BorderThickness="1" Height="100" Width="100" RenderTransformOrigin="0.5,0.5">
        <Border.RenderTransform>
            <CompositeTransform/>
        </Border.RenderTransform>
Enough already
I get this error when I drop your code into my example.`Cannot resolve TargetProperty (UIElement.RenderTransform).(CompositeTransform.ScaleX) on specified object.`
gmcalab
Ok I had to add this into my Border `<Border.RenderTransform>`` <CompositeTransform />``</Border.RenderTransform>`Now it works. Is there a way to just change the width but persist it's location, so it doesn't look like its shrinking inwards from the right but evenly on both sides?
gmcalab
You will notice in the missing XAML snippets I added it has RenderTransformOrigin="0.5,0.5" on the 2 borders. This means the scaling will occur about the centre and not the defaults. Sorry I left out some of the details.
Enough already
Very nice, thanks kind sir.
gmcalab
A: 

The problem is that both the Width and Margin properties are not DependencyProperties so they can not be animated. On workaround method to accomplish this involves adding some custom DependencyProperties to your user control code-behind which can be hooked up to the storyboard and can in turn manipulate the actual properties of the objects.

For example you could add this DependencyProperty to your UserControl which basically allow the setting of the Width property of object A:

public static readonly DependencyProperty ObjectWidthProperty = DependencyProperty.Register(
    "ObjectWidth",
    typeof(double),
    typeof(MainPage),
    new PropertyMetadata(50.0, new PropertyChangedCallback(OnObjectWidthChanged)));

public double ObjectWidth
{
    get { return (double)GetValue(ObjectWidthProperty); }
    set { SetValue(ObjectWidthProperty, value); }
}

private static void OnObjectWidthChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
    ((MainPage)d).OnObjectWidthChanged(e);
}

private void OnObjectWidthChanged(DependencyPropertyChangedEventArgs e)
{
    this.objectA.Width = this.ObjectWidth;
}

You could then add the following to your storyboard which would animate the width of objectA from 50 pixels down to 0:

<DoubleAnimation BeginTime="00:00:00"
                 Storyboard.TargetName="MyControl"
                 Storyboard.TargetProperty="ObjectWidth"
                 From="50" To="0"/>

The would also require you to add x:Name="MyControl" to your top-level UserControl. It's a little bit hacky, but it works to animate some of the underlying properties of elements that don't happen to be DependencyPropertys.

Dan Auclair
Although this works I don't want to be shrinking the entire user control...But maybe I can use it as a starting point :)
gmcalab
This doesn't actually shrink the entire user control, just objectA. The DependencyProperty is just exposed on the user control to manipulate the width of objectA since a storyboard can't directly change the Width property of objectA.
Dan Auclair