2010-07-05 12 views

Respuesta

4

Las propiedades ColumnDefinition.Width y RowDefinition.Height son del tipo GridLength, y no hay animaciones incorporadas para este tipo. Entonces, si quieres hacer eso, probablemente tendrás que crear tu propia clase GridLengthAnimation. Eso no es probablemente demasiado imposible si se toma DoubleAnimation como un ejemplo, pero no es fácil, ya sea ...

EDIT: En realidad, hay varios resultados interesantes si se busca "animación GridLength" en Google ...

http://windowsclient.net/learn/video.aspx?v=70654
http://marlongrech.wordpress.com/2007/08/20/gridlength-animation/
http://www.codeproject.com/KB/WPF/GridLengthAnimation.aspx

+0

El primer enlace me ayudó a asignarlo, gracias :) –

+4

Annnd ... el enlace "windowsclient.net" está roto. Parece que MS está desmantelando cualquier cosa anterior a Win8, o aplicando Razor de Hanlon, simplemente no le importa. – codekaizen

+0

El primer enlace fue [guardado por Wayback Machine] (http://web.archive.org/web/20111201124746/http://windowsclient.net/learn/video.aspx?v=70654) – fernio

4

me cansé de tener que jugar con XAML para animar filas y columnas de la cuadrícula hace un tiempo, así que escribió un par de métodos para hacerlo totalmente de código.

Con estos se puede ampliar/reducir el tamaño de columnas y filas de código con una línea:

Animation.AnimationHelper.AnimateGridColumnExpandCollapse(LeftColumn, true, expandedHeight, currentWidth, LeftColumn.MinWidth, 0, 200); 

Una cosa importante a destacar es el establecimiento de la animación nula en la terminación. Si no lo hace, la cuadrícula aún está bajo control de la animación cuando se completa la animación. Esto podría estar bien si la cuadrícula no tiene un divisor, pero si la cuadrícula tiene un divisor y desea poder cambiar el tamaño manualmente después de que la animación se complete, entonces debe configurar la animación como nula después de que se complete.

Aquí son los métodos:

/// <summary> 
    /// Animate expand/collapse of a grid column. 
    /// </summary> 
    /// <param name="gridColumn">The grid column to expand/collapse.</param> 
    /// <param name="expandedWidth">The expanded width.</param> 
    /// <param name="milliseconds">The milliseconds component of the duration.</param> 
    /// <param name="collapsedWidth">The width when collapsed.</param> 
    /// <param name="minWidth">The minimum width of the column.</param> 
    /// <param name="seconds">The seconds component of the duration.</param> 
    /// <param name="expand">If true, expand, otherwise collapse.</param> 
    public static void AnimateGridColumnExpandCollapse(ColumnDefinition gridColumn, bool expand, double expandedWidth, double collapsedWidth, 
     double minWidth, int seconds, int milliseconds) 
    { 
     if(expand && gridColumn.ActualWidth >= expandedWidth) 
      // It's as wide as it needs to be. 
      return; 

     if (!expand && gridColumn.ActualWidth == collapsedWidth) 
      // It's already collapsed. 
      return; 

     Storyboard storyBoard = new Storyboard(); 

     GridLengthAnimation animation = new GridLengthAnimation(); 
     animation.From = new GridLength(gridColumn.ActualWidth); 
     animation.To = new GridLength(expand ? expandedWidth : collapsedWidth); 
     animation.Duration = new TimeSpan(0, 0, 0, seconds, milliseconds); 

     // Set delegate that will fire on completion. 
     animation.Completed += delegate 
     { 
      // Set the animation to null on completion. This allows the grid to be resized manually 
      gridColumn.BeginAnimation(ColumnDefinition.WidthProperty, null); 

      // Set the final value manually. 
      gridColumn.Width = new GridLength(expand ? expandedWidth : collapsedWidth); 

      // Set the minimum width. 
      gridColumn.MinWidth = minWidth; 
     }; 

     storyBoard.Children.Add(animation); 

     Storyboard.SetTarget(animation, gridColumn); 
     Storyboard.SetTargetProperty(animation, new PropertyPath(ColumnDefinition.WidthProperty)); 
     storyBoard.Children.Add(animation); 

     // Begin the animation. 
     storyBoard.Begin(); 
    } 

    /// <summary> 
    /// Animate expand/collapse of a grid row. 
    /// </summary> 
    /// <param name="gridRow">The grid row to expand/collapse.</param> 
    /// <param name="expandedHeight">The expanded height.</param> 
    /// <param name="collapsedHeight">The collapesed height.</param> 
    /// <param name="minHeight">The minimum height.</param> 
    /// <param name="milliseconds">The milliseconds component of the duration.</param> 
    /// <param name="seconds">The seconds component of the duration.</param> 
    /// <param name="expand">If true, expand, otherwise collapse.</param> 
    public static void AnimateGridRowExpandCollapse(RowDefinition gridRow, bool expand, double expandedHeight, double collapsedHeight, double minHeight, int seconds, int milliseconds) 
    { 
     if (expand && gridRow.ActualHeight >= expandedHeight) 
      // It's as high as it needs to be. 
      return; 

     if (!expand && gridRow.ActualHeight == collapsedHeight) 
      // It's already collapsed. 
      return; 

     Storyboard storyBoard = new Storyboard(); 

     GridLengthAnimation animation = new GridLengthAnimation(); 
     animation.From = new GridLength(gridRow.ActualHeight); 
     animation.To = new GridLength(expand ? expandedHeight : collapsedHeight); 
     animation.Duration = new TimeSpan(0, 0, 0, seconds, milliseconds); 

     // Set delegate that will fire on completioon. 
     animation.Completed += delegate 
     { 
      // Set the animation to null on completion. This allows the grid to be resized manually 
      gridRow.BeginAnimation(RowDefinition.HeightProperty, null); 

      // Set the final height. 
      gridRow.Height = new GridLength(expand ? expandedHeight : collapsedHeight); 

      // Set the minimum height. 
      gridRow.MinHeight = minHeight; 
     }; 

     storyBoard.Children.Add(animation); 

     Storyboard.SetTarget(animation, gridRow); 
     Storyboard.SetTargetProperty(animation, new PropertyPath(RowDefinition.HeightProperty)); 
     storyBoard.Children.Add(animation); 

     // Begin the animation. 
     storyBoard.Begin(); 
    } 
+5

¿Cuál es la clase de gridlengthanimation que usaste? – koenmetsu

+0

Es a partir de esta respuesta https://stackoverflow.com/a/3181521/4065368 - el último enlace - acaba de modificar un bit – stambikk

4

I edificados sobre la clase AnimationHelper proporcionada por Nigel Shaw y envolvió en una reutilizable GridAnimationBehavior que se puede fijar a los elementos RowDefinition y ColumnDefinition.

/// <summary> 
/// Wraps the functionality provided by the <see cref="AnimationHelper"/> class 
/// in a behavior which can be used with the <see cref="ColumnDefinition"/> 
/// and <see cref="RowDefinition"/> types. 
/// </summary> 
public class GridAnimationBehavior : DependencyObject 
{ 
    #region Attached IsExpanded DependencyProperty 

    /// <summary> 
    /// Register the "IsExpanded" attached property and the "OnIsExpanded" callback 
    /// </summary> 
    public static readonly DependencyProperty IsExpandedProperty = 
    DependencyProperty.RegisterAttached("IsExpanded", typeof(bool), typeof(GridAnimationBehavior), 
     new FrameworkPropertyMetadata(OnIsExpandedChanged)); 

    public static void SetIsExpanded(DependencyObject dependencyObject, bool value) 
    { 
    dependencyObject.SetValue(IsExpandedProperty, value); 
    } 

    #endregion 

    #region Attached Duration DependencyProperty 

    /// <summary> 
    /// Register the "Duration" attached property 
    /// </summary> 
    public static readonly DependencyProperty DurationProperty = 
    DependencyProperty.RegisterAttached("Duration", typeof(TimeSpan), typeof(GridAnimationBehavior), 
     new FrameworkPropertyMetadata(TimeSpan.FromMilliseconds(200))); 

    public static void SetDuration(DependencyObject dependencyObject, TimeSpan value) 
    { 
    dependencyObject.SetValue(DurationProperty, value); 
    } 

    private static TimeSpan GetDuration(DependencyObject dependencyObject) 
    { 
    return (TimeSpan)dependencyObject.GetValue(DurationProperty); 
    } 

    #endregion 

    #region GridCellSize DependencyProperty 

    /// <summary> 
    /// Use a private "GridCellSize" dependency property as a temporary backing 
    /// store for the last expanded grid cell size (row height or column width). 
    /// </summary> 
    private static readonly DependencyProperty GridCellSizeProperty = 
    DependencyProperty.Register("GridCellSize", typeof(double), typeof(GridAnimationBehavior), 
     new UIPropertyMetadata(0.0)); 

    private static void SetGridCellSize(DependencyObject dependencyObject, double value) 
    { 
    dependencyObject.SetValue(GridCellSizeProperty, value); 
    } 

    private static double GetGridCellSize(DependencyObject dependencyObject) 
    { 
    return (double)dependencyObject.GetValue(GridCellSizeProperty); 
    } 

    #endregion 

    /// <summary> 
    /// Called when the attached <c>IsExpanded</c> property changed. 
    /// </summary> 
    private static void OnIsExpandedChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e) 
    { 
    var duration = GetDuration(dependencyObject); 
    var rowDefinition = dependencyObject as RowDefinition; 
    if (rowDefinition != null) 
    { 
     // The IsExpanded attached property of a RowDefinition changed 
     if ((bool)e.NewValue) 
     { 
     var expandedHeight = GetGridCellSize(rowDefinition); 
     if (expandedHeight > 0) 
     { 
      // Animate row height back to saved expanded height. 
      AnimationHelper.AnimateGridRowExpandCollapse(rowDefinition, true, expandedHeight, rowDefinition.ActualHeight, 0, duration); 
     } 
     } 
     else 
     { 
     // Save expanded height and animate row height down to zero. 
     SetGridCellSize(rowDefinition, rowDefinition.ActualHeight); 
     AnimationHelper.AnimateGridRowExpandCollapse(rowDefinition, false, rowDefinition.ActualHeight, 0, 0, duration); 
     } 
    } 

    var columnDefinition = dependencyObject as ColumnDefinition; 
    if (columnDefinition != null) 
    { 
     // The IsExpanded attached property of a ColumnDefinition changed 
     if ((bool)e.NewValue) 
     { 
     var expandedWidth = GetGridCellSize(columnDefinition); 
     if (expandedWidth > 0) 
     { 
      // Animate column width back to saved expanded width. 
      AnimationHelper.AnimateGridColumnExpandCollapse(columnDefinition, true, expandedWidth, columnDefinition.ActualWidth, 0, duration); 
     } 
     } 
     else 
     { 
     // Save expanded width and animate column width down to zero. 
     SetGridCellSize(columnDefinition, columnDefinition.ActualWidth); 
     AnimationHelper.AnimateGridColumnExpandCollapse(columnDefinition, false, columnDefinition.ActualWidth, 0, 0, duration); 
     } 
    } 
    } 
} 

Tenga en cuenta que pellizqué código de Nigel un poco para utilizar un parámetro de tipo periodo de tiempo para la duración de la animación en lugar de segundos separados y parámetros milisegundos.

Este comportamiento hace que la animación de filas/columnas de cuadrícula sea amigable con MVVM (solo XAML, no se requiere código). Ejemplo:

<Grid.RowDefinitions> 
    <RowDefinition Height="*" Behaviors:GridAnimationBehavior.IsExpanded="{Binding IsUpperPaneVisible}" /> 
    <RowDefinition Height="*" /> 
    <RowDefinition Height="*" Behaviors:GridAnimationBehavior.IsExpanded="{Binding IsLowerPaneVisible}" /> 
</Grid.RowDefinitions> 

He añadido esta respuesta porque el cartel original solicitó una solución XAML pura.

17

¿Qué tal un trabajo? ¿Por qué no colocar una cuadrícula (o cualquier otro control deseado) dentro de la fila en particular que desea animar, establecer la altura de la fila en "Auto", luego animar la altura del control. Funcionó para mí

<Grid> 
    <Grid.RowDefinitions> 
    <RowDefinition Height="30"/> 
    <RowDefinition Height="Auto"/> 
    </Grid.RowDefinitions> 
    <Button x:Name="ExpandCollapseBtn" Width="100" Click="ExpandCollapse_Click"/> 
    <WrapPanel x:Name="ToolBox" Grid.Row="1" Height="0"> 
    <Button Content="1" Width="50" Height="50"/> 
    <Button Content="2" Width="50" Height="50"/> 
    <Button Content="3" Width="50" Height="50"/> 
    <Button Content="4" Width="50" Height="50"/> 
    </WrapPanel> 
</Grid> 

Código atrás:

private bool Expanded = false; 
void ExpandCollapse_Click(object sender, RoutedEventArgs e) 
{ 
    if (Expanded) 
    { 
    var anim = new DoubleAnimation(0, (Duration)TimeSpan.FromSeconds(0.3)); 
    anim.Completed += (s, _) => Expanded = false; 
    ToolBox.BeginAnimation(ContentControl.HeightProperty, anim); 
    } 
    else 
    { 
    var anim = new DoubleAnimation(100, (Duration)TimeSpan.FromSeconds(0.3)); 
    anim.Completed += (s, _) => Expanded = true; 
    ToolBox.BeginAnimation(ContentControl.HeightProperty, anim); 
    } 
} 

que admitir que no es lo que busca. Pero es una solución rápida (suponiendo, por supuesto, que en última instancia desee colocar el UIElement dentro de la cuadrícula animando la fila de la grilla). De manera similar, puede hacerlo para el ancho de columna.

+0

este fue, de lejos, el enfoque más simple ... funcionó muy bien, la cantidad mínima de código/molestia. – AshbyEngineer

0

La biblioteca MahApps.Metro tiene un control incorporado para esto. La fuente se puede encontrar here.

<Grid> 
    <Grid.ColumnDefinitions> 
     <ColumnDefinition Width="48" x:Name="HamburgerMenuColumn" /> 
     <ColumnDefinition Width="*" /> 
    </Grid.ColumnDefinitions> 

    <Grid.Resources> 
     <Storyboard x:Key="CloseMenu" Storyboard.TargetName="HamburgerMenuColumn" Storyboard.TargetProperty="(ColumnDefinition.Width)"> 
      <metro:GridLengthAnimation To="48" Duration="00:00:00"></metro:GridLengthAnimation> 
     </Storyboard> 
    </Grid.Resources> 
</Grid> 
Cuestiones relacionadas