前言
关于软件的生命周期,是指从启动软件到关闭软件的整个过程。Application
和 Window
是两个重要的类,它们各自有自己的生命周期。理解这两个生命周期对于构建高效和可靠的 WPF 应用程序非常重要。
传送门
Application生命周期
Application,它其实是一个父类,在项目中是App.xaml文件及后台代码。说Application之前,我们先了解App.xaml是干嘛的。在上篇文章中,设置全局样式中用到了App.xaml文件,但是没有了解具体是干什么用的。
App.xaml(包括后台App.xaml.cs文件) 用于配置和管理 WPF 应用程序的启动、全局资源和事件处理。能够在应用程序生命周期的不同阶段插入自定义逻辑,并提供一个统一的方式来管理应用程序范围的资源和设置。
具体作用如下:
-
对应用程序的生存期进行跟踪并与之进行交互。
-
检测和响应未经处理的异常。(全局异常)
-
共享应用程序范围的属性和资源。
-
跟踪和管理导航。
-
检索和处理命令行参数。
-
管理独立应用程序中的窗口。
下面来看具体的生命周期涉及的方法,都是虚方法,可以重写。
方法名(函数名) | 描述 |
OnStartup | 在应用程序启动时触发。用于初始化全局资源或进行其他启动设置。 |
OnActivated | 当应用程序成为前台应用程序时发生。(表示鼠标聚焦在当前程序中) |
OnDeactivated | 当应用程序不再是前台应用程序时发生。(表示鼠标不聚焦在当前程序中) |
OnSessionEnding | 在用户通过注销或关闭操作系统而结束 Windows 会话时发生 |
OnExit | 在应用程序关闭之前发生(表示退出应用程序时) |
using System.Configuration;
using System.Data;
using System.IO;
using System.Windows;
using System.Windows.Navigation;
namespace WpfApp1
{
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
{
private string logFilePath = "app_log.txt";
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
// 初始化日志文件
InitializeLog();
Log("程序开始启动");
}
protected override void OnActivated(EventArgs e)
{
base.OnActivated(e);
Log("OnActivated被触发");
}
protected override void OnDeactivated(EventArgs e)
{
base.OnDeactivated(e);
Log("OnDeactivated被触发");
}
protected override void OnSessionEnding(SessionEndingCancelEventArgs e)
{
base.OnSessionEnding(e);
Log("OnSessionEnding被触发");
}
protected override void OnExit(ExitEventArgs e)
{
base.OnExit(e);
Log("OnExit被触发");
}
private void InitializeLog()
{
// 创建或清空日志文件
using (StreamWriter writer = new StreamWriter(logFilePath, false))
{
writer.WriteLine("Log initialized at " + DateTime.Now);
}
}
private void Log(string message)
{
// 追加日志信息到日志文件
using (StreamWriter writer = new StreamWriter(logFilePath, true))
{
writer.WriteLine($"{DateTime.Now}: {message}");
}
}
}
}
可以执行一下代码,最好是点击exe文件执行,然后看txt日志文件。(直接调试执行,不知道为什么OnActivated、OnDeactivated会被执行好多次)
实现全局异常
Application类中有个DispatcherUnhandledExceptionEventHandler事件
using System.Configuration;
using System.Data;
using System.IO;
using System.Windows;
using System.Windows.Navigation;
using System.Windows.Threading;
namespace WpfApp1
{
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
{
private string logFilePath = "app_log.txt";
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
// 初始化日志文件
InitializeLog();
Log("程序开始启动");
// 注册全局异常处理事件
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
DispatcherUnhandledException += App_DispatcherUnhandledException;
}
/// <summary>
/// 处理非UI线程引发的未处理异常
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
// 处理非UI线程引发的未处理异常
Exception ex = e.ExceptionObject as Exception;
Log($"非UI线程引发的未处理异常,报错信息: {ex}");
//退出程序
Environment.Exit(1);
}
/// <summary>
/// App_DispatcherUnhandledException
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void App_DispatcherUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e)
{
// 处理UI线程引发的未处理异常
Log($"UI线程引发的未处理异常,报错信息: {e.Exception}");
e.Handled = true; // 设置为true可以避免应用程序退出
}
private void InitializeLog()
{
// 创建或清空日志文件
using (StreamWriter writer = new StreamWriter(logFilePath, false))
{
writer.WriteLine("Log initialized at " + DateTime.Now);
}
}
private void Log(string message)
{
// 追加日志信息到日志文件
using (StreamWriter writer = new StreamWriter(logFilePath, true))
{
writer.WriteLine($"{DateTime.Now}: {message}");
}
}
}
}
具体Application生命周期图如下所示(微软官网搬过来的):
Application实现单一实例模式,以提供对其窗口、属性和资源范围服务的共享访问。 因此,每个 AppDomain只能创建类的Application一个实例。
参考微软官方文档:
Window生命周期
Window窗体,其实也是一个控件,一个Application应用实例可能会有多个窗体,这些窗体随着用户的操作被创建于内存,最后被销毁于内存。大多数情况下,销毁的请求虽然由用户发起,但最终回收内存则是由GC垃圾回收器。
窗口生存期,开始于首次实例化窗口,在这之后将打开、激活、停用直至最终关闭窗口。
方法名(函数名) | 描述 |
OnSourceInitialized | 在创建窗体源时发生。 |
Loaded | 当前窗体内部所有元素完成布局和呈现时引发此事件 |
OnActivated | 当前窗体成为前台窗体时引发此事件。(表示鼠标聚焦在当前窗体中) |
OnContentRendered | 当窗体的内容首次呈现时调用,可以用于确保窗体已经完全加载并呈现。 |
OnDeactivated | 当前窗体不再是前台窗体时发生。(表示鼠标不聚焦在当前窗体中) |
OnClosing | 窗体关闭前触发,可以在此事件中取消关闭操作。 |
OnClosed | 窗体关闭后触发,可以在此事件中释放资源。 |
第一次打开窗口时,只有当引发 Activated 后才会引发 Loaded 和 ContentRendered 事件。 记住这一点,在引发 ContentRendered 时,实际上就可认为窗口已打开。
using System.IO;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace WpfApp1
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private string logFilePath = "window_log.txt";
public MainWindow()
{
InitializeComponent();
this.Loaded += Window_Loaded;
InitializeLog();
}
protected override void OnSourceInitialized(EventArgs e)
{
base.OnSourceInitialized(e);
Log("OnSourceInitialized被执行");
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
Log("Window_Loaded被执行");
}
protected override void OnContentRendered(EventArgs e)
{
base.OnContentRendered(e);
Log("OnContentRendered被执行");
}
protected override void OnActivated(EventArgs e)
{
base.OnActivated(e);
Log("OnActivated被执行");
}
protected override void OnDeactivated(EventArgs e)
{
base.OnDeactivated(e);
Log("OnDeactivated被执行");
}
protected override void OnClosing(System.ComponentModel.CancelEventArgs e)
{
base.OnClosing(e);
Log("OnClosing被执行");
// 可以取消关闭操作
// e.Cancel = true;
}
protected override void OnClosed(EventArgs e)
{
base.OnClosed(e);
Log("OnClosed被执行");
}
private void InitializeLog()
{
// 创建或清空日志文件
using (StreamWriter writer = new StreamWriter(logFilePath, false))
{
writer.WriteLine("Log initialized at " + DateTime.Now);
}
}
private void Log(string message)
{
// 追加日志信息到日志文件
using (StreamWriter writer = new StreamWriter(logFilePath, true))
{
writer.WriteLine($"{DateTime.Now}: {message}");
}
}
}
}
某个窗口成为活动窗口后,用户可以在同一应用程序内激活其他窗口,或者激活其他应用程序。 发生这种情况时,将停用当前的活动窗口,并引发 Deactivated 事件。 同样,如果用户选择当前停用的窗口,该窗口将再次成为活动窗口,并引发 Activated 事件。
处理 Activated 和 Deactivated 的一个常见原因是为了启用和禁用仅在窗口处于活动状态时才能够运行的功能。 例如,一些窗口显示需要用户持续输入或关注的交互式内容,这些内容包括游戏和视频播放器。 以下示例是简化的视频播放器,展示如何处理 Activated 和 Deactivated 以实现此行为。
具体Window生命周期图如下所示(微软官网搬过来的):
1、首次打开窗口时,它即成为活动窗口,那Window生命周期如下所示
2、如果应用程序的窗口在显示时不激活,那Window生命周期如下所示
Window生命周期的不同,是根据ShowActivated属性的设置,在上面的代码基础上加属性。
public MainWindow()
{
InitializeComponent();
this.Loaded += Window_Loaded;
InitializeLog();
// 窗体是否激活
this.ShowActivated = false;
}
参考微软官方文档:
总结
WPF有Application和 Window是两个重要的类,它们各自有自己的生命周期。
Window 和Application应用程序都有 Activated 和 Deactivated 事件:
- Window 的 Activated 和 Deactivated 事件:这些事件在特定窗口获得或失去焦点时触发。
- Application 的 Activated 和 Deactivated 事件:这些事件在整个应用程序获得或失去焦点时触发。
- 执行顺序会不一样,当一个窗体获得焦点时,事件会按以下顺序触发:先执行Application 的 Activated 事件,再执行Window 的 Activated 事件。当一个窗体失去焦点时,事件会按以下顺序触发:先执行Window 的 Deactivated 事件,再执行Application 的 Deactivated 事件。