何时加载UIElement?

问题描述

我有以下代码

<Window x:Class="Demo.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Canvas Name="Canvas_Main" />
</Window>

public partial class MainWindow : Window
{

    public MainWindow()
    {
        InitializeComponent();
        Loaded += MainWindow_Loaded;
    }

    private void MainWindow_Loaded(Object sender,RoutedEventArgs e)
    {
        Rectangle lastRectangle = null;
        Random random = new Random(0);
        for (Int32 counter = 0; counter < 5; counter++)
        {
            Rectangle rectangle = new Rectangle();
            rectangle.Fill = Brushes.Blue;
            rectangle.Width = random.Next(100,200);
            rectangle.Height = counter * 100;
            Canvas_Main.Children.Add(rectangle);
            if (lastRectangle == null) {
                Canvas.SetLeft(rectangle,0);
                Canvas.SetTop(rectangle,0);
            }
            else
            {
                Canvas.SetLeft(rectangle,lastRectangle.ActualWidth);
                Canvas.SetTop(rectangle,0);
            }
            lastRectangle = rectangle;
        }
    }

}

由于lastRectangle.ActualWidth为0,因此无法正常工作(将每个矩形斜对角地放置)。据this answer的了解,这是因为未测量lastRectangle并且安排。

我很好奇,如果不添加到已经可见并已装载的容器中,什么时候可以进行测量和安排?

解决方法

在度量和排列布局通过之后但在呈现元素之前引发元素的Framework.Loaded事件。

在元素上调用UIElement.InvalidateMeasure进行异步布局遍历或UIElement.UpdateLayout进行同步布局遍历时,将初始化完整的布局遍历。

在您的方案中,将调用Window.Loaded事件处理程序,这意味着WindowCanvas都已加载(但未呈现)。
现在,您开始向UIElement添加新的Canvas元素。

Canvas.Children.Add应该调用InvalidateMeasure方法。由于InvalidateMeasure触发了异步布局传递,因此Canvas以及当前的子元素将被排入布局队列,并且上下文继续执行(添加更多矩形)。
由于新添加的元素已经存在待处理的布局传递,因此您应该避免手动调用UIElement.Measure(这是递归调用,考虑性能时非常昂贵)。

上下文完成后,将等待队列中等待布局通过和最终渲染的元素,并在这些元素上递归调用MeasureOverrideArrangeOverride({{ 1}}及其子对象。
结果,Canvas可以由布局系统计算。
此时,新的UIElement.RenderSize将可用。

这是添加的元素(矩形)的FrameworkElement.ActualWidth事件最终引发的时刻。

要解决您的问题,您必须改用FrameworkElement.Loaded
或等待每个Rectangle.Width事件,然后再添加下一个事件:

Rectangle.Laoded
,

该窗口的测量和布置已完成。但是,您现在正在创建新的子控件,通常直到窗口的下一个布局通过时才进行测量/布置。

您可以通过在循环末尾调用每个矩形的Measure方法来强制执行此操作。

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...