开发者

Animating Grid Column or Grid Row in XAML?

Is 开发者_如何学编程there any way I can animate Grid column width or Grid row height from XAML?


How about a work around? Why not place a grid(or any other desired control) inside the particular row that you want to animate, set the row height to "Auto", then animate the height of the control. It worked for 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>

Code behind:

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

I admit its not what you are looking for. But its a quick solution(Assuming of course that ultimately you want the UIElement placed inside the grid animated by animating the grid row). You can similarly do it for column width.


The ColumnDefinition.Width and RowDefinition.Height properties are of type GridLength, and there is no built-in animations for this type. So if you want to do that, you will probably have to create your own GridLengthAnimation class. That's probably not too impossible if you take DoubleAnimation as an example, but not easy either...

EDIT: actually, there are several interesting results if you search "GridLength animation" on 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


I got tired of having to fiddle with XAML to animate grid rows and columns a while ago so I wrote a couple of methods to do it totally from code.

With these you can expand/shrink columns and rows from code with one line:

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

One important thing to note is setting the animation to null on completion. If you don't do this, the grid is still under control of the animation when the animation is complete. This might be fine if the grid doesn't have a splitter, but if the grid has a splitter and you want to be able to resize it manually after the animation completes, then you have to set the animation to null after it completes.

Here are the methods:

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


I built upon the AnimationHelper class provided by Nigel Shaw and wrapped it in a reusable GridAnimationBehavior which can be attached to the RowDefinition and ColumnDefinition elements.

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

Note that I tweaked Nigel's code a bit to use a parameter of type TimeSpan for the animation duration instead of separate seconds and milliseconds parameters.

This behavior makes the animation of grid rows/columns MVVM friendly (XAML-only, no code behind required). Example:

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

I added this answer because the original poster asked for a pure XAML solution.


The MahApps.Metro library has a built-in control for this. The source can be found 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>


This work for me

In XAML:

<Grid >
    <Grid.RenderTransform>
        <TranslateTransform  />
    </Grid.RenderTransform>

    <Grid.RowDefinitions>
        <RowDefinition Height="*"/>
        <RowDefinition x:Name="SecondRow"  Height="100"/>
    </Grid.RowDefinitions>
</Grid >

Code Behind:

if (SecondRow.Height == new GridLength(0))
{

    Task.Run(() => {
        for(int i = 0; i <= 20; i++)
        {
            this.Dispatcher.Invoke((Action)delegate { SecondRow.Height = new GridLength(5*i); });
            Task.Delay(1).Wait();
        }
    });
}
else
{
    Task.Run(() => {
        for (int i = 20; i >= 0; i--)
        {
            this.Dispatcher.Invoke((Action)delegate { SecondRow.Height = new GridLength(5 * i); });
            Task.Delay(1).Wait();
        }
    });
}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜