wpf animation controltemplate

Logra animación "deslizable hacia abajo" en WPF



animation controltemplate (3)

Estoy intentando crear mi propia plantilla para un control Expander . Cuando se expande el control, quiero que el contenido se deslice hacia abajo lentamente.

La altura deseada del contenido no se conoce en tiempo de compilación.

Pensé que podríamos definir la diapositiva como una animación:

<Storyboard x:Key="ExpandContent"> <DoubleAnimation Storyboard.TargetName="_expanderContent" Storyboard.TargetProperty="Height" From="0.0" To="{Binding ElementName=_expanderContent,Path=DesiredHeight}" Duration="0:0:1.0" /> </Storyboard>

Pero desafortunadamente no. Recibimos un error

No se puede congelar este árbol de la línea de tiempo Storyboard para su uso en los hilos.

Parece que no podemos usar el enlace cuando definimos parámetros de animación. (Discutido también en esta pregunta )

¿Alguien tiene alguna idea sobre cómo puedo abordar esto? Desconfío de usar LayoutTransform.ScaleY , porque eso crearía una imagen distorsionada.

Esto es similar a esta pregunta , pero esta pregunta tiene una respuesta relacionada con la escritura de código subyacente, que no creo que sea posible en una plantilla de control. Me pregunto si se puede lograr una solución basada en XAML.

Por lo que vale, aquí está el estado actual de mi plantilla de control.

<ControlTemplate x:Key="ExpanderControlTemplate" TargetType="{x:Type Expander}"> <ControlTemplate.Resources> <!-- Here are the storyboards which don''t work --> <Storyboard x:Key="ExpandContent"> <DoubleAnimation Storyboard.TargetName="_expanderContent" Storyboard.TargetProperty="Height" From="0.0" To="{Binding ElementName=_expanderContent,Path=DesiredHeight}" Duration="0:0:1.0" /> </Storyboard> <Storyboard x:Key="ContractContent"> <DoubleAnimation Storyboard.TargetName="_expanderContent" Storyboard.TargetProperty="Height" From="{Binding ElementName=_expanderContent,Path=DesiredHeight}" To="0.0" Duration="0:0:1.0" /> </Storyboard> </ControlTemplate.Resources> <Grid Name="MainGrid" Background="White"> <Grid.RowDefinitions> <RowDefinition Height="Auto" /> <RowDefinition Name="ContentRow" Height="Auto" /> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="*" /> </Grid.ColumnDefinitions> <Border> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="*" /> <ColumnDefinition Width="Auto" /> </Grid.ColumnDefinitions> <ContentPresenter ContentSource="Header" /> <ToggleButton Template="{StaticResource ProductButtonExpand}" Grid.Column="1" IsChecked="{Binding Path=IsExpanded,Mode=TwoWay,RelativeSource={RelativeSource TemplatedParent}}" /> <Rectangle Grid.ColumnSpan="2" Fill="#FFDADADA" Height="1" Margin="8,0,8,2" VerticalAlignment="Bottom"/> </Grid> </Border> <ContentPresenter Grid.Row="1" HorizontalAlignment="Stretch" Name="_expanderContent"> </ContentPresenter> </Grid> <ControlTemplate.Triggers> <Trigger Property="IsExpanded" Value="True"> <Setter TargetName="_expanderContent" Property="Height" Value="{Binding ElementName=_expanderContent,Path=DesiredHeight}" /> <!-- Here is where I would activate the storyboard if they did work --> <Trigger.EnterActions> <!--<BeginStoryboard Storyboard="{StaticResource ExpandContent}"/>--> </Trigger.EnterActions> <Trigger.ExitActions> <!--<BeginStoryboard x:Name="ContractContent_BeginStoryboard" Storyboard="{StaticResource ContractContent}"/>--> </Trigger.ExitActions> </Trigger> <Trigger Property="IsExpanded" Value="False"> <Setter TargetName="_expanderContent" Property="Height" Value="0" /> </Trigger> </ControlTemplate.Triggers> </ControlTemplate>


Esta es una vieja pregunta, pero tuve problemas con esto hoy, así que supongo que publicar mi solución valdría la pena:

Tuve que animar la propiedad Height de una fila de la grilla (deslizando hacia arriba y hacia abajo), pero necesitaba un enlace dinámico para que la fila volviera a deslizarse a la misma posición que antes.

Encontré esta respuesta muy útil (después de luchar infructuosamente contra XAML): http://go4answers.webhost4life.com/Question/found-solution-work-protected-override-190845.aspx

A veces, hacer cosas en el código subyacente es simplemente más simple:

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();

La clase GridLengthAnimation se puede encontrar aquí: http://social.msdn.microsoft.com/forums/en-US/wpf/thread/da47a4b8-4d39-4d6e-a570-7dbe51a842e4/


Hay una solución lista para usar y solo XAML en CodeProject :

Los estilos:

<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"); } }

Dupliqué el estilo para tener una versión horizontal y vertical y omití los botones ToggleButton, pero puedes obtenerlo fácilmente de la publicación original.


Si puede usar Interactions con FluidLayout ( Blend 4 SDK ) tiene suerte, es realmente útil para esas animaciones de fantasía.

Primero configure el contenido CP''s Height en 0:

<ContentPresenter Grid.Row="1" HorizontalAlignment="Stretch" x:Name="_expanderContent" Height="0"/>

Para animar esto, la Height solo necesita ser animada a NaN en el VisualState que representa el estado expandido (las animaciones no discretas no le permiten usar 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> <!-- ... --->

Eso debería ser todo lo que sea necesario, el diseño fluido creará la transición para usted desde allí.

Si tiene una solución de código subyacente que estaría bien, incluso puede usar código subyacente en diccionarios como este:

<!-- 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 } }