【问题标题】:What's the difference between the Window.Loaded and Window.ContentRendered eventsWindow.Loaded 和 Window.ContentRendered 事件有什么区别
【发布时间】:2013-08-29 10:51:03
【问题描述】:

WPF 中的Window.LoadedWindow.ContentRendered 事件有什么区别?是先调用ContentRendered 事件吗?

Window.ContentRendered事件的描述here只是说

在呈现窗口内容后发生。

Window.Loaded 事件的描述 here

在元素布局、渲染并准备好交互时发生。

我有一个案例,我想将窗口的MaxHeight 设置为显示我的窗口的屏幕工作区域的高度。我应该参加哪个活动?

编辑:

我想我找到了我要找的东西,但我现在更加困惑了。 Loaded 事件首先发生,然后ContentRendered 事件发生。在 Chris Sells 和 Ian Griffiths 的 Programming WPF 一书中,它说Loaded 事件是

在窗口显示之前触发

虽然 'ContentRendered` 事件是

在可视化呈现窗口内容时引发。

这与 MSDN 文档中关于 Loaded 事件的说法相矛盾:

在元素布局、渲染并准备好交互时发生。

这现在更令人困惑了。

【问题讨论】:

    标签: wpf events window


    【解决方案1】:

    如果您访问此链接 https://docs.microsoft.com/en-us/previous-versions/dotnet/netframework-4.0/ms748948(v=vs.100)#window-lifetime-events 并向下滚动到 Window Lifetime Events,它将显示事件顺序。

    打开:

    1. SourceInitiated
    2. 已激活
    3. 已加载
    4. 内容渲染

    关闭:

    1. 结束
    2. 停用
    3. 关闭

    【讨论】:

    • 感谢您提供此信息,它是旧信息,但解决了我在使用 WPF 和多线程时遇到的很多问题:D
    【解决方案2】:

    我认为这两个事件之间几乎没有区别。为了理解这一点,我创建了一个简单的操作示例:

    XAML

    <Window x:Class="LoadedAndContentRendered.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            Name="MyWindow"
            Title="MainWindow" Height="1000" Width="525"
            WindowStartupLocation="CenterScreen"
            ContentRendered="Window_ContentRendered"     
            Loaded="Window_Loaded">
    
        <Grid Name="RootGrid">        
        </Grid>
    </Window>
    

    Code behind

    private void Window_ContentRendered(object sender, EventArgs e)
    {
        MessageBox.Show("ContentRendered");
    }
    
    private void Window_Loaded(object sender, RoutedEventArgs e)
    {
        MessageBox.Show("Loaded");
    }   
    

    在这种情况下,消息Loaded 出现在消息ContentRendered 之后的第一个位置。这证实了文档中的信息。

    一般来说,在 WPF 中,如果元素满足以下条件,就会触发 Loaded 事件:

    已布局、渲染并准备好进行交互。

    由于在 WPF 中 Window 是同一个元素,但它通常应该是排列在根面板中的内容(例如:Grid)。因此,要监视Window 的内容并创建ContentRendered 事件。来自 MSDN 的备注:

    如果窗口没有内容,则不会引发此事件。

    也就是说,如果我们创建一个Window:

    <Window x:Class="LoadedAndContentRendered.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Name="MyWindow"        
        ContentRendered="Window_ContentRendered" 
        Loaded="Window_Loaded" />
    

    它只适用于Loaded 事件。

    关于访问Window 中的元素,它们的工作方式相同。让我们在Window 的主要Grid 中创建一个Label。在这两种情况下,我们都成功获得了对Width的访问权限:

    private void Window_ContentRendered(object sender, EventArgs e)
    {
        MessageBox.Show("ContentRendered: " + SampleLabel.Width.ToString());
    }
    
    private void Window_Loaded(object sender, RoutedEventArgs e)
    {
        MessageBox.Show("Loaded: " + SampleLabel.Width.ToString());
    }   
    

    至于StylesTemplates,现阶段已成功应用,在这些事件中我们将能够访问它们。

    例如,我们要添加一个Button

    private void Window_ContentRendered(object sender, EventArgs e)
    {
        MessageBox.Show("ContentRendered: " + SampleLabel.Width.ToString());
    
        Button b1 = new Button();
        b1.Content = "ContentRendered Button";
        RootGrid.Children.Add(b1);
        b1.Height = 25;
        b1.Width = 200;
        b1.HorizontalAlignment = HorizontalAlignment.Right;
    }
    
    private void Window_Loaded(object sender, RoutedEventArgs e)
    {
        MessageBox.Show("Loaded: " + SampleLabel.Width.ToString());
    
        Button b1 = new Button();
        b1.Content = "Loaded Button";
        RootGrid.Children.Add(b1);
        b1.Height = 25;
        b1.Width = 200;
        b1.HorizontalAlignment = HorizontalAlignment.Left;
    }
    

    Loaded 事件的情况下,ButtonWindow 出现时立即添加到Grid。在ContentRendered 事件的情况下,Button 添加到Grid 之后,其所有内容都会出现。

    因此,如果您想在加载 Window 之前添加项目或更改,您必须使用 Loaded 事件。如果要进行与Window 的内容相关的操作,例如截图,则需要使用事件ContentRendered

    【讨论】:

    • 有兴趣看看当窗口重绘时会引发什么事件,例如,最小化窗口然后恢复它。窗口的Loaded 事件是否再次引发? ContentRendered 是唯一一个被提升的吗?
    • @Tony Vitabile:是的,这些事件将在Window 的开头起作用一次(最小化和恢复不受影响)。通常,Loaded 会在项目从逻辑树中物理移除并再次引入时第二次触发,然后它会触发。
    • 我一直在对此进行测试,根据我的经验,Loaded 事件在窗口渲染之前触发,而ContentRendered 事件在窗口渲染后触发。有趣的是,当Loaded 触发时,布局和测量通道似乎是完整的,因为设置了ActualHeightActualWidth。只是还没有绘制窗口。
    • @Tony Vitabile:是的,这是可能的。我给你的答案不合适?如果您对答案有任何疑问,请询问,因为我可能会遗漏一些东西。
    • 行为就是行为。我只是想确保我了解正在发生的事情,以便我可以将代码放在正确的位置。谢谢你,你帮了很多忙。
    【解决方案3】:

    如果您使用数据绑定,则需要使用 ContentRendered 事件。

    对于下面的代码,当引发 Loaded 事件时,Header 为 NULL。 但是,当引发 ContentRendered 事件时,Header 会获取其值。

    <MenuItem Header="{Binding NewGame_Name}" Command="{Binding NewGameCommand}" />
    

    【讨论】:

    • 这是重要的区别。绑定完成。因此,当您在 Loaded 事件中注册时,注册到诸如 textchanged、checkboxchanged 之类的处理程序会被触发。但是在注册 ContentRendered 时已经被触发过一次。
    【解决方案4】:

    这不是关于Window.ContentRenderedWindow.Loaded 之间的区别,而是关于如何使用Window.Loaded 事件:

    我用它来避免所有需要很长时间才能出现的应用程序中的闪屏。

        // initializing my main window
        public MyAppMainWindow()
        {
            InitializeComponent();
    
            // Set the event
            this.ContentRendered += MyAppMainWindow_ContentRendered;
        }
    
        private void MyAppMainWindow_ContentRendered(object sender, EventArgs e)
        {
            // ... comes up quick when the controls are loaded and rendered
    
            // unset the event
            this.ContentRendered -= MyAppMainWindow_ContentRendered;
    
            // ... make the time comsuming init stuff here
        }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-03-12
      • 2011-02-11
      • 2013-03-04
      • 2010-11-09
      • 1970-01-01
      • 2022-07-28
      • 2012-01-27
      相关资源
      最近更新 更多