WPF MeasureOverride And ArrangeOverride

在UIElement布局的时候,我们要知道父窗体或父控件要给子控件分配多少空间,换句话说子控件需要父控件提供多少空间给它。

这些步骤在什么时候发生呢?

这些过程来自UIElement的Measure 和Arrange,所以我们来重写这两个方法。看看里面到底做了些什么。

父MeasureOverride 和ArrangeOverride

Measure(测量),此方法实现:父元素从其自身的 MeasureCore 实现调用此方法以形成递归布局更新。

其中Measure方法传入的参数是availableSize,这个availableSize是一个Size类型,表示的是父元素可以提供的大小。

下面用实例来讲解。

新建一个PlotPanel类.

   1:  public class PlotPanel:Panel
   2:      {
   3:         protected override Size MeasureOverride(Size availableSize)
   4:         {
   5:             Size size=new Size();
   6:             foreach (UIElement child in InternalChildren)
   7:             {
   8:                 child.Measure(availableSize);
   9:                 size = child.DesiredSize;
  10:             }
  11:             return size;
  12:         }
  13:   
  14:         protected override Size ArrangeOverride(Size finalSize)
  15:         {
  16:             foreach (UIElement child in InternalChildren)
  17:             {
  18:                 double x = 50;
  19:                 double y = 50;
  20:                 child.Arrange(new Rect(new Point(x,y),child.DesiredSize));
  21:             }
  22:             return finalSize;
  23:         }
  24:      }

这里的PlotPanel类如果有子控件的话,他就会去测量和安排子控件的空间大小。

假如我们给PlotPanel一个子控件为Button,它想要一个宽度和高度为50的大小。

PlotPanel先会调用MeasureOverride方法,然后会传给他一个availableSize(可用大小),假如这里的PlotPanel的高度和宽度为400,那么这个availableSize就为(400,400)。

这里的创建一个size对象( Size size=new Size()),目的是用来返回一个子控件希望的大小(DesiredSize),这时PlotPanel就会foreach子控件,得它DesiredSize这个值。

根据上面的数据这里的child.DesiredSize应该是(50,50).父控件得到子控件的希望大小后,就要去安排(Arrange)它,也就是在界面上呈现它的大小。

这时PlotPanel就会去调用ArrangeOverride方法。传入的参数是finalSize(finalRect为Parent最后给你的大小),这里有人会说,子控件已经返回一个希望的大小,为什么还要一个finalSize呢?这里我的理解是,父容器虽然知道了你想要的大小,但你想要的DesiredSize比我给你的availableSize小的话,我还是会给你availableSize大小,也就是这里的 finalSize大小为availableSize,反之给DesiredSize.

根据上面所说这里我们的finalSize为(400,400)。然后执行child.Arrange();画一个矩形;他的宽和高为(50,50),但还是返回了一个finalSize(400,400).这里真正要看的是child的RenderSize(child实际的大小)。这里的child.RenderSize为(50,50).

在xaml中。

<Window x:Class="MeasureAndArrange.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:my="clr-namespace:MeasureAndArrange"
        Title="MainWindow" Height="400" Width="400">
        <my:PlotPanel>
            <Button Width="50" Height="50"/>
        </my:PlotPanel>
</Window>

我们运行看看:
image

假如,PlotPanel给这个Button一个(30,30)的大小,那会怎么样呢?

我们修改下代码:

   1:  protected override Size MeasureOverride(Size availableSize)
   2:         {
   3:             foreach (UIElement child in InternalChildren)
   4:             {
   5:                 child.Measure(new Size(30,30));
   6:             }
   7:             return availableSize;
   8:         }

运行过程:

availableSize=(400,400)

finalSize=(400,400)

child.DesiredSize=(50,50)

child.RenderSize=(30,30)

效果图:

image

很明显,Button被clip了.

父和子的MeasureOverride 和ArrangeOverride

我们先新建两个类MyPanelParent和MyPanel。

先看代码:

父类

   1:  public class MyPanelParent:Panel
   2:      {
   3:         protected override Size MeasureOverride(Size availableSize)
   4:         {
   5:             foreach (UIElement child in InternalChildren)
   6:             {
   7:                 child.Measure(new Size(120,120));
   8:             }
   9:             return availableSize;
  10:         }
  11:   
  12:         protected override Size ArrangeOverride(Size finalSize)
  13:         {
  14:             double x = 0;
  15:             foreach (UIElement child in InternalChildren)
  16:             {
  17:                 child.Arrange(new Rect(x,0,100,100));
  18:                 x += 100;
  19:             }
  20:             return finalSize;
  21:         }
  22:      }

子类

   1:   public class MyPanel:Panel
   2:     {
   3:         protected override Size MeasureOverride(Size availableSize)
   4:         {
   5:             foreach (UIElement child in InternalChildren)
   6:             {
   7:                 child.Measure(availableSize);
   8:             }
   9:             return new Size(50,50);
  10:         }
  11:   
  12:         protected override Size ArrangeOverride(Size finalSize)
  13:         {
  14:             double x = 0;
  15:             foreach (UIElement child in InternalChildren)
  16:             {
  17:                child.Arrange(new Rect(new Point(x,0),child.DesiredSize));
  18:                 x += child.DesiredSize.Width;
  19:             }
  20:             return new Size(80,80);
  21:         }
  22:     }

xaml中

   1:  <Window x:Class="MeasureAndArrange.MainWindow"
   2:          xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   3:          xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   4:          xmlns:my="clr-namespace:MeasureAndArrange"
   5:          Title="MainWindow" Height="600" Width="600">
   6:      <Grid>
   7:          <Canvas>
   8:              <my:MyPanelParent Height="400" Width="400" Background="Linen" Canvas.Left="10" Canvas.Top="10">
   9:                  <my:MyPanel  Background="Red" />
  10:                  <my:MyPanel  Background="Red" />
  11:              </my:MyPanelParent>
  12:          </Canvas>
  13:      </Grid>
  14:  </Window>

这里的大概流程是首先MyPanelParent执行MeasureOverride方法,当执行到 child.Measure时,就会去调用MyPanel的MeasureOverride方法。

通过递归一个一个实现测量。

然后MyPanelParent执行ArrangeOverride方法,同样执行到child.Arrange时,调用MyPanel的ArrangeOverride方法。

下面分析下运行过程:

Measure

首先MyPanelParent的MeasureOverride方法传进来一个availableSize大小为(400,400),因为我们在xaml中设置了这个可用大小。

然后测量给MyPanel一个大小为(120,120),从这里可以看出child.Measure(new Size(120,120));于是调用MyPanel的MeasureOverride(Size availableSize)

availableSize为(120,120).而MyPanel返回了一个DesireSize为(50,50)给MyPanelParent。

Arrange

MyPanelParent这时传进了一个finalSize(400,400),这个大小也就是MyPanelParent的availableSize,然后给MyPanel安排一个矩形宽高为(100,100)。

这时调用MyPanel的ArrangeOverride传入的finalSize(100,100),这个finalSize就是MyPanelParent对child安排的(Arrange)大小。而这里MyPanel它说,

它只要呈现(80,80)的Size。这个大小在MyPanelParent给他的finalSize大小之内,所以最后的RenderSize为(80,80).

图:

imageimage

image

这里主要参考MSDN和其他人的blogs.

posted @ 2011-04-22 12:57  Lee's Blog  阅读(7548)  评论(2编辑  收藏  举报