C# WPF 应用程序生命周期及焦点相关事件说明文档

一、引言

对于初学者来说,理解 C# WPF(Windows Presentation Foundation)应用程序的生命周期以及焦点相关事件是非常重要的,这有助于准确把握应用程序在不同阶段的运行情况,以及处理好用户与界面交互时的各种状态变化。本说明文档将详细介绍这些关键知识点,帮助你更好地进行 WPF 应用开发。

二、应用程序生命周期事件

(一)Startup 事件
  • 触发时机:在应用程序启动后,App 类(对应 App.xaml.cs 文件)的构造函数执行完毕后紧接着触发。
  • 详细说明:这是整个应用程序启动流程中一个重要的设置阶段,通常用于创建并显示应用程序的主窗口。除此之外,还可以依据启动时传入的参数(通过 StartupEventArgs 传递)来决定初始显示的界面内容、加载不同的配置,或者注册一些全局的事件处理程序,例如全局的异常处理,以便捕获应用程序运行过程中出现的未处理异常。示例代码如下:
    private void Application_Startup(object sender, StartupEventArgs e)
    {
    MainWindow mainWindow = new MainWindow();
    if (e.Args.Contains("--dark-theme"))
    {
        mainWindow.ApplyDarkTheme(); // 假设是自定义的应用黑暗主题方法
    }
    mainWindow.Show();
    // 注册全局异常处理
    AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
    }
    private void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
    {
    // 记录异常信息等处理操作
    Exception ex = (Exception)e.ExceptionObject;
    _logger.LogError($"未处理异常: {ex.Message}");
    }
(二)LoadCompleted 事件
  • 触发时机:在已经加载、分析并开始呈现应用程序中的导航器导航到的内容时发生。
  • 详细说明:这个事件适合在应用程序内容加载完成后进行一系列初始化或配置工作。比如可以在这里初始化全局的数据,调整应用程序整体的默认显示状态,确保应用在内容呈现后处于合适的初始状态,方便用户后续操作。例如,若应用中有一些基于数据驱动的界面展示,可在此时加载并初始化相关数据,使得界面能正确显示信息。
(三)NavigationFailed 事件
  • 触发时机:当应用程序中的导航器在导航到所请求内容时出现错误的情况下触发。
  • 详细说明:在开发具有页面导航功能的 WPF 应用时,这个事件非常关键。它可以用于捕获导航过程中出现的各种错误,然后依据具体情况向用户显示相应的错误提示信息,让用户了解出现了什么问题,同时也可以将这些错误信息记录到日志文件中,方便后续排查和修复问题。例如,如果导航到一个不存在的页面或者网络请求页面内容出现故障时,就会触发此事件,进而执行相应的错误处理逻辑。
(四)NavigationProgress 事件
  • 触发时机:在由应用程序中的导航器管理的下载过程中定期发生,用于提供导航进度信息。
  • 详细说明:当应用程序进行页面导航且涉及到需要一定时间下载资源(如网络页面加载、较大的本地资源加载等)时,该事件会周期性地触发。开发人员可以利用这个特性,在界面上向用户展示进度条或者其他形式的提示信息,告知用户当前的导航进度,提升用户体验,避免用户因长时间等待而产生疑惑或不耐烦的情绪。
(五)SessionEnding 事件
  • 触发时机:在用户通过注销或关闭操作系统而结束 Windows 会话时触发。
  • 详细说明:应用程序可以利用这个事件来保存当前的状态、数据等关键信息,以便下次启动时能够恢复到之前的使用状态。例如,保存用户正在编辑的文档内容、界面的布局设置、当前应用的一些个性化配置等,确保用户不会因意外关闭系统而丢失重要数据或需要重新进行繁琐的配置。
(六)Shutdown 事件
  • 触发时机:当应用程序所有的窗口都关闭后触发。
  • 详细说明:这是整个应用程序结束运行前的最后一个环节,用于进行全局的资源清理工作。比如关闭应用程序级别的缓存,将一些全局配置信息持久化到磁盘(如保存用户的个性化设置、应用的运行参数等),处理日志(如将内存中的日志信息批量写入到磁盘文件中,确保所有运行记录都被妥善保存)等收尾操作,确保应用程序结束运行时不会遗留一些未处理的资源占用问题,同时也保证了数据的完整性和可恢复性。

三、窗口生命周期事件

(一)Loaded 事件
  • 触发时机:窗口被实例化并即将显示在屏幕上时触发。
  • 详细说明:在这个阶段,主要进行与窗口界面元素紧密相关的初始化操作。可以设置控件的初始属性,例如根据用户权限设置某些按钮是否可见、是否可用;进行数据绑定的初始化,将界面控件与后台数据模型关联起来,使得数据能够正确显示和交互;还可以设置一些动画效果的初始参数,让窗口在初次显示时带有特定的动画过渡,增强视觉效果和用户体验。以下是一些示例:

    private void Window_Loaded(object sender, RoutedEventArgs e)
    {
    if (CurrentUser.Role!= "Administrator")
    {
        deleteUserButton.Visibility = Visibility.Collapsed;
    }
    // 进行数据绑定初始化等操作
    myViewModel = new MyViewModel();
    this.DataContext = myViewModel;
    
    DoubleAnimation fadeInAnimation = new DoubleAnimation
    {
        From = 0,
        To = 1,
        Duration = TimeSpan.FromSeconds(1)
    };
    this.BeginAnimation(UIElement.OpacityProperty, fadeInAnimation);
    }
(二)SourceInitialized 事件
  • 触发时机:在操作系统给窗口分配句柄的时候触发。
  • 详细说明:此事件为开发者提供了获取窗口句柄的机会,从而可以进行一些与底层操作系统相关的操作或特殊设置。例如,可以通过窗口句柄来修改窗口的样式,调整窗口的初始位置、大小等,实现一些超出常规 WPF 控件属性设置所能达到的效果,满足特定的界面需求或与系统环境更好地适配。
(三)ContentRendered 事件
  • 触发时机:在窗体内容渲染后触发,也就是窗口的所有内容已经在屏幕上绘制完成。
  • 详细说明:此时可以根据窗口实际呈现出来的情况进行进一步的操作。比如,根据窗口的大小动态调整内部控件的布局,确保在不同分辨率或窗口大小下界面的美观性和可用性;或者自动将焦点聚焦到某个特定的关键控件上,方便用户直接进行操作,提升交互的便捷性。
(四)Closing 事件
  • 触发时机:当用户尝试关闭窗口(比如点击窗口的关闭按钮、选择菜单中的关闭选项或者使用系统快捷键(如 Alt + F4)等方式)时触发。
  • 详细说明:这是窗口关闭前的重要确认和预处理阶段。可以在这里全面检查窗口相关的数据状态,例如在文档编辑类应用中,检查文档是否有未保存的更改,如果有则提示用户保存;也可以提前释放一些当前不再需要但仍占用资源的对象,比如关闭正在使用的网络连接、数据库连接(如果连接是与该窗口相关且不再需要)等。并且,通过设置 CancelEventArgs.Cancel 属性为 true,可以取消窗口的关闭操作,代码示例如下:
    private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
    {
    if (Document.HasUnsavedChanges())
    {
        MessageBoxResult result = MessageBox.Show("文档有未保存的更改,是否保存?", "提示", MessageBoxButton.YesNoCancel);
        if (result == MessageBoxResult.Cancel)
        {
            e.Cancel = true;
        }
        else if (result == MessageBoxResult.Yes)
        {
            Document.Save();
        }
    }
    if (networkClient!= null && networkClient.IsConnected())
    {
        networkClient.Disconnect();
    }
    }
(五)Closed 事件
  • 触发时机:在窗口成功关闭(即 Closing 事件没有被取消)后触发。
  • 详细说明:这个事件主要用于最后的资源清理工作,重点是释放与窗口相关的各种非托管资源(例如通过 Interop 调用的一些外部资源),以及取消注册一些与该窗口相关的事件处理程序等,避免出现资源泄漏或者不必要的事件重复触发等问题,确保窗口关闭后相关资源被妥善处理。例如:
    [DllImport("gdi32.dll")]
    private static extern bool DeleteObject(IntPtr hObject);
    private IntPtr _customDrawingHandle; // 假设这是通过 API 创建的绘图资源句柄
    private void Window_Closed(object sender, EventArgs e)
    {
    if (_customDrawingHandle!= IntPtr.Zero)
    {
        DeleteObject(_customDrawingHandle);
        _customDrawingHandle = IntPtr.Zero;
    }
    // 取消注册事件处理程序
    myButton.Click -= MyButton_Click;
    }
(六)Unloaded 事件
  • 触发时机:当窗口从元素树中删除时引发此事件,通常在窗口关闭或被移除时触发。
  • 详细说明:主要用于释放与该窗口关联的各类资源,比如取消之前订阅的各种事件,清理自定义控件所占用的资源等,确保窗口在从界面元素体系中移除后,不会遗留任何可能影响应用程序性能或产生异常的资源占用情况。

四、焦点相关事件

(一)LostFocus 事件
  • 触发时机:当窗体或其内部的控件失去键盘焦点时触发。例如,当用户在一个文本框中输入内容后,点击窗体外部区域,文本框就会失去焦点,此时会触发 LostFocus 事件。
  • 详细说明:开发者可以利用这个事件来执行一些特定的操作,比如当文本框失去焦点时,验证用户输入的内容是否符合格式要求,如果不符合则给出提示信息;或者在某些控件失去焦点时,更新与之相关联的其他控件的显示状态等,实现灵活的界面交互逻辑。
(二)Deactivated 事件
  • 触发时机:当窗体从活动状态变为非活动状态,即成为后台窗口时触发,不仅仅是失去焦点,还包括用户切换到其他应用程序或其他窗口等情况。
  • 详细说明:可以在这个事件中进行一些与窗口状态切换相关的处理,比如暂停一些正在进行的后台操作(如定时刷新数据等),避免不必要的资源消耗或者数据冲突;或者改变窗口的显示样式(如降低亮度等),以提示用户当前窗口处于非活动状态。
(三)GotFocus 事件
  • 触发时机:当窗体或其内部的控件获得键盘焦点时触发。比如,当用户点击一个文本框,使其能够接收键盘输入时,该文本框会触发 GotFocus 事件。
  • 详细说明:常用于对获得焦点的控件进行特定的初始化或设置操作,例如当文本框获得焦点时,自动选中其中的所有文本内容,方便用户直接进行修改;或者改变控件的外观(如边框颜色变深等),突出显示当前获得焦点的控件,提升用户操作的视觉引导性。
(四)Activated 事件
  • 触发时机:当窗体从非活动状态变为活动状态,即成为前台窗口时触发,通常在用户切换回该应用程序或该窗口时发生。
  • 详细说明:可以在这个事件中恢复一些之前在 Deactivated 事件中暂停的操作,比如重新启动定时刷新数据的任务;或者将窗口恢复到之前的正常显示状态(如恢复亮度等),确保用户切换回窗口时能立即正常使用。

以下是一个简单的示例代码,演示如何在 WPF 窗口中使用这些焦点相关事件:

<Window x:Class="WpfApp1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="450" Width="800"
        LostFocus="Window_LostFocus"
        GotFocus="Window_GotFocus"
        Activated="Window_Activated"
        Deactivated="Window_Deactivated">
    <Grid>
        <TextBlock x:Name="txtb" HorizontalAlignment="Center" VerticalAlignment="Center" FontSize="24"/>
    </Grid>
</Window>
public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
    }

    private void Window_LostFocus(object sender, RoutedEventArgs e)
{
        txtb.Text = "Lost Focus";
    }

    private void Window_GotFocus(object sender, RoutedEventArgs e)
{
        txtb.Text = "Got Focus";
    }

    private void Window_Activated(object sender, EventArgs e)
{
        txtb.Text = "Activated";
    }

    private void Window_Deactivated(object sender, EventArgs e)
{
        txtb.Text = "Deactivated";
    }
}

在上述代码中,在 XAML 中为窗口的相关焦点事件绑定了事件处理方法,然后在后台代码中实现了这些事件处理方法,当相应的事件触发时,会在窗口中的文本块中显示相应的提示信息,方便初学者直观地理解这些事件的触发时机和执行效果。

希望这份说明文档能够帮助初学者系统地理解 C# WPF 应用程序生命周期及焦点相关事件的知识,在实际的开发学习过程中更好地运用这些内容来构建功能完善、交互友好的应用程序。