【问题标题】:At what point is a UIElement loaded?什么时候加载 UIElement?
【发布时间】:2021-01-19 17:18:12
【问题描述】:

我有以下代码:

<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 尚未测量和排列。

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

【问题讨论】:

    标签: c# wpf loaded


    【解决方案1】:

    窗口的测量和布置已经完成。但是您现在正在创建新的子控件,通常在窗口的下一个布局通过之前不会对其进行测量/排列。

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

    【讨论】:

    • 谢谢。但是,那么是什么原因导致一个窗口的布局方法被调用呢?我无法想象这会在计时器中运行,但添加或删除孩子不会触发布局?如果您能扩展您的答案,将不胜感激。
    • 在每个 UI 应用程序的顶部都有一个 while 循环,它反复运行直到应用程序退出。布局过程是 WPF 循环执行的阶段之一。 (为了抢占您可能的下一个问题,避免阻塞 CPU 内核,循环将在某个时候处于空闲状态,直到操作系统提示它继续,例如通过通知用户输入。)
    【解决方案2】:

    元素的Framework.Loaded 事件在measure 和arrange layout pass 之后触发,但在元素渲染之前。

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

    在您的场景中,调用了 Window.Loadedevent 处理程序,这意味着 WindowCanvas 都已加载(但未呈现)。
    现在您开始向Canvas 添加新的UIElement 元素。

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

    一旦上下文完成,队列中等待布局传递和最终渲染的元素将被处理,并在这些元素上递归调用 MeasureOverrideArrangeOverrideCanvas 及其子元素)。
    结果UIElement.RenderSize可以被布局系统计算出来。
    此时,新的FrameworkElement.ActualWidth 将可用。

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

    要解决您的问题,您必须改用Rectangle.Width
    或者在添加下一个之前等待每个Rectangle.Laoded 事件:

    private int ShapeCount { get; set; }
    private const int MaxShapes = 5;
    private Point ShapeBPosition { get; set; }
    
    private void MainWindow_Loaded(Object sender, RoutedEventArgs e)
    {
      this.ShapePosition = new Point();
      AddRectangle(this.ShapePosition);
    }
    
    private void AddRectangle(Point position)
    {
      Random random = new Random();
      Rectangle rectangle = new Rectangle();
      rectangle.Fill = Brushes.Blue;
    
      rectangle.Width = random.Next(100, 200);
      rectangle.Height = ++this.ShapeCount * 100;
    
      Canvas_Main.Children.Add(rectangle);
      Canvas.SetLeft(rectangle, position.X);
      Canvas.SetTop(rectangle, position.Y);
    
      rectangle.Loaded += OnRectangleLoaded;
    }
    
    private void OnRectangleLoaded(object sender, RoutedEventArgs e)
    {
      var rectangle = sender as Rectangle;
    
      rectangle.Loaded -= OnRectangleLoaded;
      if (this.ShapeCount == MainWindow.MaxShapes)
      {
        return;
      }
    
      // this.ShapePosition is struct => modify copy
      Point position = this.ShapePosition; 
      position.Offset(rectangle.ActualWidth, 0);
      this.ShapePosition = position;
      AddRectangle(this.ShapePosition);
    }
    

    【讨论】:

    • 谢谢你 - 你给了我很多方法和事件来查找更多细节。
    猜你喜欢
    • 1970-01-01
    • 2013-02-27
    • 2011-11-25
    • 1970-01-01
    • 2019-06-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多