在 Windows 窗体中加载与显示事件

Posted

技术标签:

【中文标题】在 Windows 窗体中加载与显示事件【英文标题】:Load vs. Shown events in Windows Forms 【发布时间】:2010-09-28 16:02:05 【问题描述】:

希望我只是遗漏了一些明显的东西,但我试图弄清楚 Windows Forms 中 Load 和 Shown 事件之间的差异。

传统上,我只使用了 Load(或者实际上是 OnLoad,因为我认为重写一个方法比依赖设计者自己连接一个事件更干净),因为它在所有版本的 .NET 中都可用.在 .NET 2.0 中引入了 Shown 事件。

现在,如果您在 MSDN 文档中查看这些描述(“加载:在第一次显示表单之前发生。”,“显示:在第一次显示表单时发生。”)它 听起来应该发生 Load 事件,然后表单应该变得可见,然后应该发生 Shown 事件;两者的结合使您可以在表格可见之前和之后执行一些任务。有道理,对吧?

然而,实验表明 Shown 事件总是发生在 Load 事件之前,无论何时我尝试它(并且两者都发生在表单变得可见之前)。然而,每当我发现一个页面讨论这些事件的触发顺序时,他们总是会列出最先触发的 Load 事件。

我是不是快疯了,还是我错过了什么? (如果它们确实几乎同时发生,那么为什么首先添加 Shown 事件?)

(我目前在显示表单之前和之后做某事的解决方案是使用 OnLoad 来处理“显示之前”的内容,并为“显示之后”的内容启动一个短时间的一次性计时器。这可以正常工作并且可靠,但它有点难看,我希望有一个更清洁的解决方案。但看起来 Shown 事件不是它。)

【问题讨论】:

【参考方案1】:

我肯定知道的一件事是在 InitializeComponent 上的所有内容都完成并且表单显示之后执行显示的事件,并且显示您应该根据位置将移动对象的代码放在表单上其他对象

用一个空项目做一个快速测试:

Public Class Form1

Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
    MsgBox("load") 'form is still visible = false
End Sub

Private Sub Form1_Shown(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Shown
    MsgBox("shown") ' form is now visible = true
End Sub
End Class

【讨论】:

【参考方案2】:

我刚刚在 Shown 之前检查并加载了火灾。

你的方法显然有问题。

【讨论】:

【参考方案3】:

好的,我想我已经弄清楚了现在到底发生了什么,以及我的困惑来自哪里(尽管不是为什么它会那样做)。看起来 Shown 事件实际上发生在 内部 Load 事件中。

鉴于此代码:

 protected override OnLoad(EventArgs e)

    MessageBox.Show("Enter Load");
    base.OnLoad(e);
    MessageBox.Show("Exit Load");


protected override OnShown(EventArgs e)

    MessageBox.Show("Enter Shown");
    base.OnShown(e);
    MessageBox.Show("Exit Shown");

然后消息按以下顺序显示:

    输入负载 输入显示 退出显示 退出加载

Visible 属性在所有四种情况下都是 True,但在这些情况中没有是在屏幕上实际可见的表单(绘制)。

真正奇怪的是,如果我注释掉“退出加载”消息框,那么表单确实出现在屏幕上出现“输入显示”消息之前。它似乎是在基本 OnLoad 调用之后执行的代码,它确实以某种方式反对。

【讨论】:

为了完美的实验尝试登录文件不要使用消息框,使用streamwiter或文件类【参考方案4】:

Shown 事件发生在Load 事件之后。主要区别不在于表单的可见性,而在于其状态(宽度、高度等)。

为了澄清,这里是一个例子。考虑一个默认大小为100, 200WindowStateMaximized 的表单。在Load 事件处理程序中,大小将为100, 200。但在Shown 事件处理程序中,尺寸将是您的屏幕尺寸

【讨论】:

【参考方案5】:

避免使用 MessageBox.Show() 来调试它。它泵出一个消息循环,扰乱了正常的事件流。 Load 事件由 Windows 发送 WM_SHOWWINDOW 消息触发,就在窗口变得可见之前。没有关于“您的窗口现在已完全显示”的 Windows 通知,因此 WF 设计者想出了一个技巧来生成 Shown 事件。他们使用 Control.BeginInvoke(),确保在程序再次空闲并重新进入消息循环时调用 OnShown() 方法。

这个技巧还有很多其他用途,特别是当您必须延迟由事件启动的代码的执行时。但是,在您的情况下,它会因为您使用 MessageBox.Show() 而分崩离析。它的消息循环调度使用 BeginInvoke() 注册的委托,导致 Shown 事件在显示窗口之前运行

除了 MessageBox 之外,还有许多其他方法可以获取诊断信息。 Debug.Print() 和 Console.WriteLine() 很方便,它们的输出会发送到Visual Studio Output Window,而不会对正常的事件触发序列产生任何不利影响。一个简单的breakpoint 也能创造奇迹。

【讨论】:

有道理。虽然最初的问题是由不涉及 MessageBoxes 的其他不按顺序发生的事情触发的——但它可能确实包括发送消息。关键似乎是确保在 base.OnLoad 之后没有其他代码运行。 在我的应用程序中,我设法在窗口出现后但在 Shown 事件之前的短暂时间内打开菜单。通过在构造函数中禁用窗口并在显示的事件中重新启用来解决。【参考方案6】:

这是我追踪的事件顺序。希望这将有助于其他人决定他们想如何调用或设置他们的自定义事件处理

跟踪的事件

表格 - 客户规模已更改:2010 年 8 月 14 日上午 10:40:28 表单 - 已添加控件 - button1:2010 年 8 月 14 日上午 10:40:29 表单 - 构造函数:2010 年 8 月 14 日上午 10:40:29 表单 - 句柄创建时间:2010 年 8 月 14 日上午 10:40:29 表格 - 无效:2010 年 8 月 14 日上午 10:40:29 表单 - 表单加载事件:2010 年 8 月 14 日上午 10:40:29 表格 - 已加载:2010 年 8 月 14 日上午 10:40:29 表单 - 创建控件:2010 年 8 月 14 日上午 10:40:29 表格 - OnActivated : 8/14/2010 10:40:29 AM 表格 - 显示时间:2010 年 8 月 14 日上午 10:40:29 表格 - OnPaint:2010 年 8 月 14 日上午 10:40:29 表格 - 无效:2010 年 8 月 14 日上午 10:40:29 表格 - OnPaint:2010 年 8 月 14 日上午 10:40:29

【讨论】:

以上是关于在 Windows 窗体中加载与显示事件的主要内容,如果未能解决你的问题,请参考以下文章

在 Nhibernate 中加载与获取

怎样在一个窗体中加入时间,显示现在的时间啊?

windows窗体生命周期中发生的事件

在 Windows 窗体设计器中加载窗体时出现“找不到类型”错误

windo form 窗体布局方式

如何在 Windows 窗体应用程序中显示 MFC 控件?