WPF中文网

Window窗体的生命周期

Window窗体,其实也是一个控件,一个Application应用实例可能会有多个窗体,这些窗体随着用户的操作被创建于内存,最后被销毁于内存。大多数情况下,销毁的请求虽然由用户发起,但最终回收内存则是由GC垃圾回收器在干活儿。

我们以HelloWorld应用为例,打开MainWindowp窗体的源代码,切换至后端代码,可以发现MainWindow继承于Window类。

namespace HelloWorld
{
    /// <summary>
    /// MainWindow.xaml 的交互逻辑
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }
    }
}

将鼠标的光标放至“Window”字符串上面,按下F12 ,就可以导航到Window类的定义页面,我们会发现Window类又继承于ContentControl类,下面是MainWindow主窗体的整个继承路线。

MainWindow->Window->ContentControl->Control->FrameworkElement->UIElement->Visual->DependencyObject->DispatcherObject

在这里,我们并不打算将MainWindow的所有知识详尽,因为这必须要将它一路继承下来的所有父亲都要交代清楚,我们只关注MainWinow的生命周期。所以我们在MainWinow的构造函数中写下如下代码:

namespace HelloWorld
{
    /// <summary>
    /// MainWindow.xaml 的交互逻辑
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();

            this.SourceInitialized += (s, e) => Console.WriteLine("1.MainWindow的SourceInitialized被执行");

            this.Activated += (s, e) => Console.WriteLine("2.MainWindow的Activated被执行");

            this.Loaded += (s, e) => Console.WriteLine("3.MainWindow的Loaded被执行");

            this.ContentRendered += (s, e) => Console.WriteLine("4.MainWindow的ContentRendered被执行");

            this.Deactivated += (s, e) => Console.WriteLine("5.MainWindow的Deactivated被执行");

            this.Closing += (s, e) => Console.WriteLine("6.MainWindow的Closing被执行");

            this.Closed += (s, e) => Console.WriteLine("7.MainWindow的Closed被执行");

            this.Unloaded += (s, e) => Console.WriteLine("8.MainWindow的Unloaded被执行");

        }
    }
}

然后我们直接F5调试,待主窗体显示后,直接关闭主窗体,观察输出(Ctrl+Alt+Q)结果。Application的生命周期和主窗体的生命周期是充满交织的,首先是Application的OnStartup,然后是主窗体的SourceInitialized,然后依次执行了Application的OnActivated和MainWindow的Activated,最后直到主窗体Closed,才轮到Application的OnExit。

1.OnStartup被触发
“HelloWorld.exe”(CLR v4.0.30319: HelloWorld.exe): 已加载“C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\PresentationFramework.Aero2\v4.0_4.0.0.0__31bf3856ad364e35\PresentationFramework.Aero2.dll”。已跳过加载符号。模块进行了优化,并且调试器选项“仅我的代码”已启用。
“HelloWorld.exe”(CLR v4.0.30319: HelloWorld.exe): 已加载“C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\PresentationCore.resources\v4.0_4.0.0.0_zh-Hans_31bf3856ad364e35\PresentationCore.resources.dll”。模块已生成,不包含符号。
1.MainWindow的SourceInitialized被执行
“HelloWorld.exe”(CLR v4.0.30319: HelloWorld.exe): 已加载“c:\program files\microsoft visual studio\2022\community\common7\ide\commonextensions\microsoft\xamldiagnostics\Framework\x86\Microsoft.VisualStudio.DesignTools.WpfTap.dll”。已跳过加载符号。模块进行了优化,并且调试器选项“仅我的代码”已启用。
“HelloWorld.exe”(CLR v4.0.30319: HelloWorld.exe): 已加载“C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Runtime.Serialization\v4.0_4.0.0.0__b77a5c561934e089\System.Runtime.Serialization.dll”。已跳过加载符号。模块进行了优化,并且调试器选项“仅我的代码”已启用。
“HelloWorld.exe”(CLR v4.0.30319: HelloWorld.exe): 已加载“C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\SMDiagnostics\v4.0_4.0.0.0__b77a5c561934e089\SMDiagnostics.dll”。已跳过加载符号。模块进行了优化,并且调试器选项“仅我的代码”已启用。
“HelloWorld.exe”(CLR v4.0.30319: HelloWorld.exe): 已加载“C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.ServiceModel.Internals\v4.0_4.0.0.0__31bf3856ad364e35\System.ServiceModel.Internals.dll”。已跳过加载符号。模块进行了优化,并且调试器选项“仅我的代码”已启用。
2.OnActivated被触发
2.MainWindow的Activated被执行
“HelloWorld.exe”(CLR v4.0.30319: HelloWorld.exe): 已加载“C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Runtime.Serialization.resources\v4.0_4.0.0.0_zh-Hans_b77a5c561934e089\System.Runtime.Serialization.resources.dll”。模块已生成,不包含符号。
“HelloWorld.exe”(CLR v4.0.30319: HelloWorld.exe): 已加载“C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\UIAutomationTypes\v4.0_4.0.0.0__31bf3856ad364e35\UIAutomationTypes.dll”。已跳过加载符号。模块进行了优化,并且调试器选项“仅我的代码”已启用。
“HelloWorld.exe”(CLR v4.0.30319: HelloWorld.exe): 已加载“C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\UIAutomationProvider\v4.0_4.0.0.0__31bf3856ad364e35\UIAutomationProvider.dll”。已跳过加载符号。模块进行了优化,并且调试器选项“仅我的代码”已启用。
3.MainWindow的Loaded被执行
4.MainWindow的ContentRendered被执行
5.MainWindow的Closing被执行
6.MainWindow的Deactivated被执行
3.OnDeactivated被触发
7.MainWindow的Closed被执行
4.OnExit被触发
程序“[4808] HelloWorld.exe”已退出,返回值为 0 (0x0)。

我们来单独看看主窗体的生命周期,在上述输出结果中寻找带“MainWindow”的字符串,可以发现如下的输出结果。

  • 1.MainWindow的SourceInitialized被执行
  • 2.MainWindow的Activated被执行
  • 3.MainWindow的Loaded被执行
  • 4.MainWindow的ContentRendered被执行
  • 5.MainWindow的Closing被执行
  • 6.MainWindow的Deactivated被执行
  • 7.MainWindow的Closed被执行

观察这些输出结果,与我们订阅事件的代码顺序一致,唯独少了Unloaded的结果输出。因为Unloaded事件没有被触发。下面我们将分析一下这些事件分别代表什么含义。

SourceInitialized创建窗体源时引发此事件
Activated当前窗体成为前台窗体时引发此事件
Loaded当前窗体内部所有元素完成布局和呈现时引发此事件
ContentRendered当前窗体的内容呈现之后引发此事件
Closing当前窗体关闭之前引发此事件
Deactivated当前窗体成为后台窗体时引发此事件
Closed当前窗体关闭之后引发此事件
Unloaded当前窗体从元素树中删除时引发此事件

由此我们可以得出结论,Window窗体的生命周期应如下图所示:

在了解窗体的生命周期之后,我们就可以在它不同的生命周期处理一些不同的业务。例如在Application或Window的创建时加载一些本地设置,在窗体关闭或应用程序退出时保存一些本地设置。在了解了Window窗体的生命周期之后,我们再来学习Window窗体本身由哪些构成。

下一节,我们将讨论Window窗体的组成。

当前课程源码下载:(注明:本站所有源代码请按标题搜索)

文件名:003-《Window的生命周期》-源代码
链接:https://pan.baidu.com/s/1yu-q4tUtl0poLVgmcMfgBA
提取码:wpff

——重庆教主 2023年8月11日

关于软件的生命周期,是指从启动软件到关闭软件的整个过程。

<Application x:Class="HelloWorld.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:local="clr-namespace:HelloWorld"
             StartupUri="MainWindow.xaml">
    <Application.Resources>
         
    </Application.Resources>
</Application>

我们观察一下HelloWorld应用程序的App.xaml前端代码,StartupUri="MainWindow.xaml"表示本程序第一个启动的窗体是MainWindow,在前端代码中按下F7将进入到App的后端代码界面,在后端代码中,键入下面这些代码。

using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;

namespace HelloWorld
{
    /// <summary>
    /// App.xaml 的交互逻辑
    /// </summary>
    public partial class App : Application
    {
        protected override void OnStartup(StartupEventArgs e)
        {
            base.OnStartup(e);
            Console.WriteLine("1.OnStartup被触发");
        }

        protected override void OnActivated(EventArgs e)
        {
            base.OnActivated(e);
            Console.WriteLine("2.OnActivated被触发");
        }

        protected override void OnDeactivated(EventArgs e)
        {
            base.OnDeactivated(e);
            Console.WriteLine("3.OnDeactivated被触发");
        }

        protected override void OnExit(ExitEventArgs e)
        {
            base.OnExit(e);
            Console.WriteLine("4.OnExit被触发");
        }
    }
}

然后我们按下F5启动调试流程,最小化窗体,还原窗体,最后关闭窗体。回到开发界面,在菜单栏-视图-输出中找到如下信息(或快捷键Ctro+Alt+Q)

1.OnStartup被触发
“HelloWorld.exe”(CLR v4.0.30319: HelloWorld.exe): 已加载“C:\Windows\Microsoft.Net\assembly\GAC_MSIL\PresentationFramework.Aero2\v4.0_4.0.0.0__31bf3856ad364e35\PresentationFramework.Aero2.dll”。已跳过加载符号。模块进行了优化,并且调试器选项“仅我的代码”已启用。
“HelloWorld.exe”(CLR v4.0.30319: HelloWorld.exe): 已加载“C:\Windows\Microsoft.Net\assembly\GAC_MSIL\PresentationCore.resources\v4.0_4.0.0.0_zh-Hans_31bf3856ad364e35\PresentationCore.resources.dll”。模块已生成,不包含符号。
“HelloWorld.exe”(CLR v4.0.30319: HelloWorld.exe): 已加载“c:\program files\microsoft visual studio\2022\community\common7\ide\commonextensions\microsoft\xamldiagnostics\Framework\x86\Microsoft.VisualStudio.DesignTools.WpfTap.dll”。已跳过加载符号。模块进行了优化,并且调试器选项“仅我的代码”已启用。
“HelloWorld.exe”(CLR v4.0.30319: HelloWorld.exe): 已加载“C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System.Runtime.Serialization\v4.0_4.0.0.0__b77a5c561934e089\System.Runtime.Serialization.dll”。已跳过加载符号。模块进行了优化,并且调试器选项“仅我的代码”已启用。
“HelloWorld.exe”(CLR v4.0.30319: HelloWorld.exe): 已加载“C:\Windows\Microsoft.Net\assembly\GAC_MSIL\SMDiagnostics\v4.0_4.0.0.0__b77a5c561934e089\SMDiagnostics.dll”。已跳过加载符号。模块进行了优化,并且调试器选项“仅我的代码”已启用。
“HelloWorld.exe”(CLR v4.0.30319: HelloWorld.exe): 已加载“C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System.ServiceModel.Internals\v4.0_4.0.0.0__31bf3856ad364e35\System.ServiceModel.Internals.dll”。已跳过加载符号。模块进行了优化,并且调试器选项“仅我的代码”已启用。
2.OnActivated被触发
“HelloWorld.exe”(CLR v4.0.30319: HelloWorld.exe): 已加载“C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System.Runtime.Serialization.resources\v4.0_4.0.0.0_zh-Hans_b77a5c561934e089\System.Runtime.Serialization.resources.dll”。模块已生成,不包含符号。
“HelloWorld.exe”(CLR v4.0.30319: HelloWorld.exe): 已加载“C:\Windows\Microsoft.Net\assembly\GAC_MSIL\UIAutomationTypes\v4.0_4.0.0.0__31bf3856ad364e35\UIAutomationTypes.dll”。已跳过加载符号。模块进行了优化,并且调试器选项“仅我的代码”已启用。
“HelloWorld.exe”(CLR v4.0.30319: HelloWorld.exe): 已加载“C:\Windows\Microsoft.Net\assembly\GAC_MSIL\UIAutomationProvider\v4.0_4.0.0.0__31bf3856ad364e35\UIAutomationProvider.dll”。已跳过加载符号。模块进行了优化,并且调试器选项“仅我的代码”已启用。
3.OnDeactivated被触发
2.OnActivated被触发
3.OnDeactivated被触发
4.OnExit被触发
程序“[10696] HelloWorld.exe”已退出,返回值为 0 (0x0)。

我们可以明确的看到,在输入信息中所打印的信息按如下的顺序显示:

  • 1.OnStartup被触发
  • 2.OnActivated被触发
  • 3.OnDeactivated被触发
  • 2.OnActivated被触发
  • 3.OnDeactivated被触发
  • 4.OnExit被触发

由于我们可以得出结论,当我们启动WPF应用时,首先被执行的是OnStartup方法,其次是OnActivated方法,如果我们把当前应用最小化或切换到其它程序时,这时OnDeactivated会被执行,再切回来时再次执行OnActivated方法,最后,当我们关闭程序时,OnDeactivated会再次被执行,最后执行的是OnExit方法。

Application的生命周期

OnStartup->OnActivated->OnDeactivated->OnExit
  • OnStartup:表示启动应用程序时
  • OnActivated:表示激活应用程序时
  • OnDeactivated:表示由激活状态变为非激活状态时
  • OnExit:表示退出应用程序时

这只是Application的生命周期,事实上,由于Application总是会启动一个界面(窗体),而窗体也会有自己的生命周期。下一节,我们将讨论一下窗体(Window)的生命周期。

当前课程源码下载:(注明:本站所有源代码请按标题搜索)

文件名:002-《Application的生命周期》-源代码
链接:https://pan.baidu.com/s/1yu-q4tUtl0poLVgmcMfgBA
提取码:wpff

——重庆教主 2023年8月10日

copyright @重庆教主 WPF中文网 联系站长:(QQ)23611316 (微信)movieclip (QQ群).NET小白课堂:864486030 | 本文由WPF中文网原创发布,谢绝转载 渝ICP备2023009518号-1