2010-07-05 19 views

risposta

4

Le proprietà ColumnDefinition.Width e RowDefinition.Height sono di tipo GridLength e non sono presenti animazioni incorporate per questo tipo. Quindi, se vuoi farlo, probabilmente dovrai creare la tua classe GridLengthAnimation. Questo non è probabilmente troppo impossibile se si prende DoubleAnimation come esempio, ma non è facile o ...

EDIT: in realtà, ci sono diversi risultati interessanti se si cerca "l'animazione GridLength" su 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

Il primo collegamento mi ha aiutato ad assegnare, grazie :) –

+4

Annnd ... il collegamento "windowsclient.net" è rotto. Sembra che la SM stia smantellando qualsiasi cosa pre-Win8, o applicando il rasoio di Hanlon, semplicemente non se ne preoccupa. – codekaizen

+0

Il primo collegamento è stato [salvato dalla Wayback Machine] (http://web.archive.org/web/20111201124746/http://windowsclient.net/learn/video.aspx?v=70654) – fernio

4

mi sono stancato di dover armeggiare con XAML per animare le righe e le colonne della griglia qualche tempo fa così ho scritto un paio di metodi per farlo totalmente dal codice.

Con questi è possibile espandere/shrink colonne e righe da codice con una sola riga:

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

Una cosa importante da notare è l'impostazione l'animazione per nulla al termine. Se non lo fai, la griglia è ancora sotto controllo dell'animazione quando l'animazione è completa. Questo potrebbe andare bene se la griglia non ha uno splitter, ma se la griglia ha uno splitter e vuoi essere in grado di ridimensionarlo manualmente dopo il completamento dell'animazione, devi impostare l'animazione su null dopo il completamento.

Questi sono i metodi:

/// <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

qual è la classe gridlengthanimation che hai usato? – koenmetsu

+0

È da questa risposta https://stackoverflow.com/a/3181521/4065368 - l'ultimo link - appena modificato un bit – stambikk

4

ho costruito dalla classe AnimationHelper fornito da Nigel Shaw e avvolto in un riutilizzabile GridAnimationBehavior associabile agli elementi RowDefinition e 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); 
     } 
    } 
    } 
} 

noti che ho ottimizzato il codice di Nigel un po 'di utilizzare un parametro di tipo TimeSpan per la durata animazione anziché secondi separati e parametri millisecondi.

Questo comportamento rende l'animazione delle righe/colonne della griglia MVVM amichevole (solo XAML, nessun codice dietro richiesto). Esempio:

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

Ho aggiunto questa risposta perché il poster originale richiedeva una soluzione XAML pura.

17

Che ne dici di un lavoro in giro? Perché non posizionare una griglia (o qualsiasi altro controllo desiderato) all'interno della particolare riga che si desidera animare, impostare l'altezza della riga su "Auto", quindi animare l'altezza del controllo. Ha funzionato per me.

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

codice dietro:

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

ammetto la sua non è quello che stai cercando. Ma è una soluzione rapida (supponendo che alla fine si desideri che l'UIElement sia inserito nella griglia animato animando la riga della griglia). Puoi farlo allo stesso modo per la larghezza della colonna.

+0

questo era di gran lunga l'approccio più semplice ... ha funzionato molto bene, meno quantità di codice/seccature. – AshbyEngineer

0

La libreria MahApps.Metro dispone di un controllo incorporato per questo. La fonte può essere trovata 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> 
Problemi correlati