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>
假如,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)
效果图:
很明显,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).
图:
这里主要参考MSDN和其他人的blogs.
作者:dingli
出处:http://www.cnblogs.com/dingli/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。