技术开发 频道

深入解读Silverlight的布局原理

  我们看到FrameworkElement定义了一个布局的框架:其中MeasureOverride就是测量过程,ArrangeOverride就是排列过程。我们一般的控件都继承自它,所以就可能建立起了这样一个递归系统。

  Silverlight要求,要定位可视元素,必须将它们放置于Panel或者其他容器对象中。因为Panel定义了确定如何在屏幕上绘制Panel元素的Children集合成员的布局行为。

  自定义Panel很简单,从Panel派生并重写其MeasureOverride和ArrangeOverride方法即可。在两个方法中,都可以获取Children属性(下面列举SDK例子)。

  测量:

protected override Size MeasureOverride(Size availableSize)
{
    
int i =0;
    foreach (FrameworkElement child in Children)
    {
        
if (i < 9) child.Measure(new Size(100, 100));
        
else child.Measure(new Size(0, 0));
        i
++;
    }
    return
new Size(300,300);
}

 

  在MeasureOverride中,必须调用每个子元素的Measure方法,传递该容器可以分配的空间,确定分配多少空间给子级,然后像上级返回整个容器需要的空间大小。该方法会触发子元素执行内部的MeasureOverride方法,如此递归。

  其实我们想到,测量过程可以是不必要的,排列可以完全按照自己的逻辑进行,它可以不管子元素到底需要多大,但是这样肯定是不行的,除非容器额外了解很多子元素的情况,否则可能排列出的效果很差。那么测量其实是为排列提供一个重要的参考信息:

  DesiredSize,DesiredSize可以理解为:行政长官给在发放通知的时候给下级一个指标,但是下级会有一个实际需求,而DesiredSize相当于指标和需求之间的最小值。具体的说,DesiredSize在Measure发放之后计算的,布局系统基于传递给Measure的availableSize和元素的固有大小去定子元素的DesiredSize,一般讲DesiredSize设置为两种的最小值。这个值可以在排列的时候作为一个参考依据。

  排列:

  在排列处理过程中,必须确定每个子级的布局槽的位置并设置面板的最终大小。当你计算好位置之后,调用每个元素的Arrange方法就可以了,其参数是一个Rect对象,这个对象可以表示一个元素的空间大小和位置。如下所示:

    image.Arrange(new Rect(10, 10, 150, 150));

 

  该句将元素image放在x为10,y为10的位置,所占宽度和高度都为150的大小空间。

  定义一个Panel就这里简单,这里是定义Panel规则的地方,你可以设计不同的规则,根据不同的附加属性或依赖属性进行布局。Grid,Canvas,StackPanel等都是经过这样定义的。

  所以,你可以在这里设计很复杂的计算,使元素按不同的方式布局排列。

  神秘绝招-让Panel的子元素都具有某种行为

  如果只是能摆放元素的位置和限制大小,这样未免没有多大的用途,所有元素都只是一个摆设。

  但是,想到我们前面花了大量篇幅分析得Behavior,试想,我们能不能给容器设置某种行为,让它把这种行为附加到每一个元素,从而让容器还能控制元素的行为呢?哈哈,你太聪明了,没错,这是完全可以的!

  由于讲过Behavior的原理,以及布局的原理,这里我就不分析代码了,附上源文件供参考,代码写得很粗糙,仅作演示用,请勿用在项目中,这里还是讲一下大概思路:

  首先定义了一个派生自Panel的类PhotoWallPanel,在ArrangeOverride中将每个元素随机选择了一个位置,并随机旋转了一个角度;

  然后定义一个Behavior,它作用于PhotoWallPanel,遍历其中的每一个元素,给每个元素添加一个鼠标经过和离开的效果,然后附加到PhotoWallPanel上,值得注意的是:

  <i:Interaction.Behaviors>
            
<local:ShowPictureBehavior/>
        
</i:Interaction.Behaviors>
    
</local:PhotoWallPanel>

 

  在XAML中,附加Behavior的代码应该位于Panel的结尾之前,或者在其他字元素之后,这样才能保证将行为添加到元素上,因为XAML是顺序解析的,在前面的元素首先实例化其对象,如果放在前面,这个时候Panel的子元素还没有初始化。

  总结

  写了这么多,有的人觉得烦了,确实也是这么点东西写那么多废话,但是自定义Panel以及Behavior这些都是重要的基本概念,如果你想深入Silverlight开发,或者很在意UI,这些应该是必须掌握的。希望本篇能对学习Silverlight的朋友有点启示。

  值得重视的是,我们看到Silverlight中附加属性的重要性,基本上容器控件都有定义附加属性,而Behavior也是利用附加属性,很值得好好研究,并在项目中充分利用。

  Blend截图:

1
 

  实际效果

1  

0
相关文章