周银辉
WPF的布局功能异常强大,当有时我们会有一些奇怪的需求:布局之间的切换。比如动态地将控件在UniformGrid布局和StackPanel布局之间切换。这种需求是有意义的,比如Blend中的DesignWorkspace和AnimationWorkspace切换功能。WPF可以轻松做到这一点。
1, 无过渡动画的直接切换:
这没有什么讨论的必要了,要将控件从源布局切换到目标布局,只需要简单地将该控件从源布局控件中删除然后添加到目标布局控件中就可以了。
2, 有过度动画的切换:
这才是我们这篇文章要讨论的。为了明白我在说什么,你可以先下载这个程序运行一下以观察该类型的切换。
基本原理
假设有几个Button控件要在UniformGrid布局和StackPanel布局之间切换,我们可以这样来实现:
就其中的一个按钮btn1而言,无论btn1被加载到哪个面板中,都是先从上一个面板中将btn1删除,然后在调用另外一个面板的Children.Add方法,该动作都是瞬间完成的,除非修改面板内部逻辑,不可以产生动画,更不可能有过度效果.
那么事实上,我们既不将btn1放到UnifromGrid中也不把它放到StackPanel中,而是将它放到一个Canvas中.并且Canvas与UnifromGrid以及StackPanel重合. 而放到UnifromGrid或StackPanel中的是另外一个元素:我们称为LayoutToLayoutTarget(Target),切换布局的时候,事实上,我们是将Target从一个面板中删除然后在加到另一个面板中,与此同时,我们的btn1参照Target在新面板中的位置和大小来作为其动画的目标值,然后利用动画来到变化到此值.当由于其他原因(比如用户改变窗口大小等)导致Target位置或大小发生变化时,我们的btn1也会立即做出相应的变化(而不采用动画来渐变了).可以看出btn1始终在Canvas中,只是大小和位置发生了变化,而让人产生了错觉
这里的btn1只是一个特例,为了让所有的控件都可以达到这个效果而不仅仅是Button,我们就使用LayoutToLayoutHost来包含其他控件(host.Child=myBtn).Host的行为为上面的btn1完全一样.
public class LayoutToLayoutTarget : Border
LayoutToLayoutTarget用来指示LayoutToLayoutHost的大小以及将放置在什么位置.当布局变化时,先将LayoutToLayoutTarget变化到合适的位置和大小, 然后LayoutToLayoutHost再根据LayoutToLayoutTarget的大小和位置来进行动画
public class LayoutToLayoutHost : Border
LayoutToLayoutHost用来Host控件. LayoutToLayoutTarget用来指示LayoutToLayoutHost的大小以及将放置在什么位置.当布局变化时,先将LayoutToLayoutTarget变化到合适的位置和大小, 然后LayoutToLayoutHost再根据LayoutToLayoutTarget的大小和位置来进行动画
使用前的准备工作是:
//初始化一个LayoutToLayoutTarget
LayoutToLayoutTarget target = new LayoutToLayoutTarget();
//设置目标大小
target.MinWidth = 80;
target.MinHeight = 50;
this.targets.Add(target);
//将其放置在源布局控件中
this.uniformGrid1.Children.Add(target);
//初始化一个LayoutToLayoutTarget
LayoutToLayoutHost host = new LayoutToLayoutHost();
//先将host放在Canvas中
this.canvas1.Children.Add(host);
//demoButton为我们要改变布局的控件
Button demoButton = new Button();
demoButton.Content = "# " + i;
//将它放在host中
host.Child = demoButton;
//将host和target联系起来
host.BindToTarget(target);
改变布局时:
//将target从源布局控件中删除
this.uniformGrid1.Children.Remove(target)
//将target添加到目标布局控件中
this.stackPanel1.Children.Add(target);
//开始动画
host.BeginAnimating(false);
如果觉得一头雾水的话,可以在这里下载DEMO谢谢