默认情况下ListBox每当ItemsSource发生改变时,UI显示数据一闪而出。我们不希望显示的这么生硬,希望中间能有个过渡动画。当你有这样的需求时,这篇文章能帮助你。
先上效果图:
本篇文章要求你对FrameworkElement的 MeasureOverride(Size availableSize) 和ArrangeOverride(Size finalSize)方法有所了解,这里只做简单介绍。
MeasureOverride:此方法为布局中控件所需要的空间大小进行评估。
ArrangeOverride:此方法作用在于为面板子控件提供布局空间即排列子控件并返回自身大小。
解剖功能:
1.动画。很明显动画是必须得写的而且是加在Panel的子元素上。
private void TryStartAnimation(UIElement child, double y) { Storyboard storyboard = new Storyboard(); DoubleAnimation doubleAnimation = new DoubleAnimation { From = child.DesiredSize.Width / 2, To = 0d, Duration = LoadedAnimationDuration }; Storyboard.SetTarget(doubleAnimation, child); Storyboard.SetTargetProperty(doubleAnimation, new PropertyPath("(UIElement.RenderTransform).(CompositeTransform.TranslateX)")); storyboard.Children.Add(doubleAnimation); child.Opacity = 1; storyboard.Begin(); }
2.动画播放控制。动画是要求要按子元素的排列顺序开启的,首先想到的就是得有 队列(Queue)。动画开启是要有时间间隔的,不能一起开启啊,所以还得有个timer。
DispatcherTimer _timer = null; Queue<Action> _actions = new Queue<Action>();
队列里放的是执行开启动画的委托。
timer每到时间就从队列里取出一委托,将其执行。
void _timer_Tick(object sender, EventArgs e) { if (_actions.Count == 0) { _timer.Stop(); } else { _actions.Dequeue().Invoke(); } }
3. 分配子元素位置和绑定动画
protected override Size MeasureOverride(Size availableSize) { var result = new Size(); foreach (UIElement child in Children) { child.Measure(new Size(availableSize.Width, availableSize.Height)); result.Height += child.DesiredSize.Height; result.Width = Math.Max(result.Width, child.DesiredSize.Width); //因为我们会改变子元素的Opacity,所以将CacheMode赋值为BitmapCache child.CacheMode = new BitmapCache(); } _actions.Clear(); return result; } protected override Size ArrangeOverride(Size finalSize) { var point = new Point(); foreach (UIElement child in Children) { child.Arrange(new Rect(point, child.DesiredSize)); //设置子元素的Transform var transform = child.RenderTransform as CompositeTransform; child.RenderTransform = transform ?? new CompositeTransform(); //将动画委托放入队列 _actions.Enqueue(() => TryStartAnimation(child, point.Y)); point.Y += child.DesiredSize.Height; //先将子元素隐藏 child.Opacity = 0; } //开启动画 _timer.Start(); return finalSize; }
一个子元素带有加载动画的Panel就做出来了。
本篇文章主要讲的是思路,SlideInPanel 对子元素只做了垂直排版和从右到左滑动的动画,大家可以对它无限扩展,做成支持垂直,横向,或者做成像WrapPanel那样都可以实现。动画也可以变得啊。
希望这篇文章能对你有所帮助。
源码和Sample在这里