I am attempting to create my own template for an Expander
control. When the control is expanded, I want the content to slide down slowly.
The desired he
If you can use Interactions
with FluidLayout
(Blend 4 SDK) you are in luck, it's really useful for those fancy animation things.
First set the content CP's Height to 0:
<ContentPresenter Grid.Row="1"
HorizontalAlignment="Stretch"
x:Name="_expanderContent"
Height="0"/>
To animate this, the Height
just needs to be animated to NaN
in the VisualState
that represents the expanded state (non-discrete animations would not let you use NaN
):
xmlns:is="http://schemas.microsoft.com/expression/2010/interactions"
<Grid x:Name="MainGrid" Background="White">
<VisualStateManager.CustomVisualStateManager>
<is:ExtendedVisualStateManager/>
</VisualStateManager.CustomVisualStateManager>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="ExpansionStates" is:ExtendedVisualStateManager.UseFluidLayout="True">
<VisualStateGroup.Transitions>
<VisualTransition GeneratedDuration="0:0:1"/>
</VisualStateGroup.Transitions>
<VisualState x:Name="Expanded">
<Storyboard>
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(FrameworkElement.Height)"
Storyboard.TargetName="_expanderContent">
<DiscreteDoubleKeyFrame KeyTime="0" Value="NaN"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Collapsed"/>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<!-- ... --->
That should be all that is necessary, the fluid layout will create the transition for you from there.
If you have a code-behind solution that would be fine, you can even use code-behind in dictionaries like this:
<!-- TestDictionary.xaml -->
<ResourceDictionary x:Class="Test.TestDictionary"
...>
//TestDictionary.xaml.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
namespace Test
{
partial class TestDictionary : ResourceDictionary
{
//Handlers and such here
}
}
This is kind of an old question but I had problems with this today, so I guess posting my solution would be worth it:
I had to animate the Height property of a grid row (sliding up and down), but needed dynamic binding so that the row would slide again to the same position as before.
I found this answer to be very helpful (after fruitlessly battling XAML): http://go4answers.webhost4life.com/Question/found-solution-work-protected-override-190845.aspx
Sometimes doing things in the code-behind is just simpler:
Storyboard sb = new Storyboard();
var animation = new GridLengthAnimation
{
Duration = new Duration(500.Milliseconds()),
From = this.myGridRow.Height,
To = new GridLength(IsGridRowVisible ? GridRowPreviousHeight : 0, GridUnitType.Pixel)
};
// Set the target of the animation
Storyboard.SetTarget(animation, this.myGridRow);
Storyboard.SetTargetProperty(animation, new PropertyPath("Height"));
// Kick the animation off
sb.Children.Add(animation);
sb.Begin();
The GridLengthAnimation class can be found here: http://social.msdn.microsoft.com/forums/en-US/wpf/thread/da47a4b8-4d39-4d6e-a570-7dbe51a842e4/
There is a ready-to-use and XAML-only solution on CodeProject:
The Styles:
<local:MultiplyConverter x:Key="MultiplyConverter" />
<Style TargetType="Expander" x:Key="VerticalSlidingEmptyExpander">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Expander}">
<ScrollViewer x:Name="ExpanderContentScrollView"
HorizontalScrollBarVisibility="Hidden"
VerticalScrollBarVisibility="Hidden"
HorizontalContentAlignment="Stretch"
VerticalContentAlignment="Top"
>
<ScrollViewer.Tag>
<system:Double>0.0</system:Double>
</ScrollViewer.Tag>
<ScrollViewer.Height>
<MultiBinding Converter="{StaticResource MultiplyConverter}">
<Binding Path="ActualHeight" ElementName="ExpanderContent"/>
<Binding Path="Tag" RelativeSource="{RelativeSource Self}" />
</MultiBinding>
</ScrollViewer.Height>
<ContentPresenter x:Name="ExpanderContent" ContentSource="Content"/>
</ScrollViewer>
<ControlTemplate.Triggers>
<Trigger Property="IsExpanded" Value="True">
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Storyboard.TargetName="ExpanderContentScrollView"
Storyboard.TargetProperty="Tag"
To="1"
Duration="0:0:0.2"/>
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
<Trigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Storyboard.TargetName="ExpanderContentScrollView"
Storyboard.TargetProperty="Tag"
To="0"
Duration="0:0:0.2"/>
</Storyboard>
</BeginStoryboard>
</Trigger.ExitActions>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="Expander" x:Key="HorizontalSlidingEmptyExpander">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Expander}">
<ScrollViewer x:Name="ExpanderContentScrollView"
HorizontalScrollBarVisibility="Hidden"
VerticalScrollBarVisibility="Hidden"
HorizontalContentAlignment="Left"
VerticalContentAlignment="Stretch"
>
<ScrollViewer.Tag>
<system:Double>0.0</system:Double>
</ScrollViewer.Tag>
<ScrollViewer.Width>
<MultiBinding Converter="{StaticResource MultiplyConverter}">
<Binding Path="ActualWidth" ElementName="ExpanderContent"/>
<Binding Path="Tag" RelativeSource="{RelativeSource Self}" />
</MultiBinding>
</ScrollViewer.Width>
<ContentPresenter x:Name="ExpanderContent" ContentSource="Content"/>
</ScrollViewer>
<ControlTemplate.Triggers>
<Trigger Property="IsExpanded" Value="True">
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Storyboard.TargetName="ExpanderContentScrollView"
Storyboard.TargetProperty="Tag"
To="1"
Duration="0:0:0.2"/>
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
<Trigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Storyboard.TargetName="ExpanderContentScrollView"
Storyboard.TargetProperty="Tag"
To="0"
Duration="0:0:0.2"/>
</Storyboard>
</BeginStoryboard>
</Trigger.ExitActions>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
MultiplyConverter:
public class MultiplyConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType,
object parameter, CultureInfo culture)
{
double result = 1.0;
for (int i = 0; i < values.Length; i++)
{
if (values[i] is double)
result *= (double)values[i];
}
return result;
}
public object[] ConvertBack(object value, Type[] targetTypes,
object parameter, CultureInfo culture)
{
throw new Exception("Not implemented");
}
}
I duplicated the Style to have a horizontal and vertical version and omitted the ToggleButtons, but you can easily get that from the original post.