Содержание статьи

Механизмы завершения работы WPF-приложения отличаются по поведению и зависят от того, какие окна открыты, какие процессы выполняются в фоне и требуется ли пользователю подтверждение. Разные подходы дают разный контроль над логикой завершения, что важно при работе с сохранением данных, незавершёнными потоками или сетевыми операциями.
При использовании Application.Current.Shutdown() приложение завершает работу принудительно. Метод Close() закрывает только текущее окно, поэтому его поведение связано с параметром ShutdownMode. Обработчик события Closing позволяет отслеживать причину закрытия и при необходимости отменять его. В ситуациях с фоновыми задачами может потребоваться остановка процессов перед завершением, иначе возникнут подвисания или ошибки.
Отдельного внимания требуют сценарии с необработанными исключениями. Через DispatcherUnhandledException можно завершить приложение аккуратно, сохранив данные или записав отчёт. Такой подход снижает риск непредсказуемого поведения при закрытии и помогает контролировать состояние программы до остановки.
Завершение работы через команду Application.Current.Shutdown()

Команда Application.Current.Shutdown() завершает процесс приложения, обходя логику отдельных окон. При вызове метод генерирует событие Exit, что позволяет выполнить финальные действия: запись логов, освобождение ресурсов, остановку подключений. Этот способ удобен при необходимости закрыть программу из любого участка кода, включая сервисные классы.
Метод принимает необязательный код завершения. Значение передаётся операционной системе и помогает фиксировать причину закрытия. Например, Shutdown(1) можно использовать для сигнализации об ошибочном сценарии, а Shutdown(0) – для штатного завершения. Код возврата полезен при запуске приложения из внешних скриптов или систем автоматизации.
При использовании Shutdown() WPF проигнорирует состояние окон и закроет их без вызова Close(). Это важно учитывать при наличии незавершённых операций в окнах: сохранение файлов, обращения к БД, активные таймеры. Чтобы предотвратить потерю данных, стоит подписаться на событие Application.Current.Exit и выполнить нужную очистку или сохранение перед завершением.
Закрытие окна с помощью метода Close() и его влияние на приложение

Метод Close() завершает работу конкретного окна и запускает последовательность событий Closing и Closed. Эти события позволяют проверить состояние данных, остановить таймеры, выключить фоновые задачи, связанных с окном. Если внутри обработчика Closing установить e.Cancel = true, окно не закроется, что помогает предотвратить потерю данных.
Поведение приложения после закрытия окна зависит от параметра ShutdownMode. Если установлено значение OnMainWindowClose, завершится весь процесс после закрытия главного окна. При OnLastWindowClose приложение будет работать до тех пор, пока не закроется последнее видимое окно. Значение OnExplicitShutdown игнорирует закрытие окон и требует явного вызова Application.Current.Shutdown().
При работе со сложными интерфейсами важно учитывать, что закрытие дочерних окон не влияет на работу других участков приложения. Это позволяет создавать дополнительные окна настроек, диалоговые формы или вспомогательные панели, не затрагивая основной процесс. Однако закрытие окна, выполняющего критичную логику, может прервать незавершённые операции, поэтому перед вызовом Close() стоит убедиться, что все процессы корректно завершены.
Обработка события Closing для контроля завершения

Событие Closing вызывается перед закрытием окна и предоставляет доступ к объекту CancelEventArgs. Через свойство e.Cancel можно остановить завершение, если требуется дождаться сохранения данных, остановить активные процессы или вывести предупреждение пользователю. Такой подход даёт возможность проверить состояние интерфейса и предотвратить некорректное завершение работы.
В обработчике стоит проверять признаки незавершённых операций: активные подключения, изменённые без сохранения поля, выполняющиеся фоновые задачи. Если закрытие недопустимо, флаг Cancel следует установить в true и выполнить необходимые действия. После успешного завершения операций можно повторно вызвать Close().
Через свойство WindowClosingEventArgs.CloseReason можно определить, что стало источником закрытия: пользователь, вызов из кода или завершение системы. Это помогает разделить логику обработки и избежать лишних проверок. В сложных приложениях такое разделение обеспечивает предсказуемую реакцию на разные сценарии закрытия.
Использование ShutdownMode и его влияние на поведение приложения
Свойство ShutdownMode определяет момент, когда приложение прекращает работу. Параметр задаётся в App.xaml или программно через Application.Current.ShutdownMode. Корректный выбор режима позволяет управлять временем завершения процесса и поведением главного окна.
Режимы отличаются тем, какие окна учитываются при остановке работы. Если интерфейс включает вспомогательные панели или диалоговые формы, неправильный режим может закрыть приложение раньше нужного момента или, наоборот, удерживать процесс в памяти без открытых окон.
| Режим | Условие завершения | Применение |
|---|---|---|
| OnLastWindowClose | Закрывается последнее открытое окно | Подходит для многооконных интерфейсов |
| OnMainWindowClose | Закрывается только главное окно | Удобно, когда вспомогательные окна не должны влиять на процесс |
| OnExplicitShutdown | Завершение только через Shutdown() | Используется при сложной логике выхода |
При работе с OnExplicitShutdown важно отслеживать моменты, когда следует вызвать Shutdown(). Если этот вызов не сделан, процесс останется активным, даже если пользователь закрыл все окна. Это необходимо учитывать при создании фоновых окон или системных служб, открывающих интерфейс по требованию.
Прерывание закрытия через Cancel в обработчике Closing
Обработчик события Closing предоставляет доступ к объекту CancelEventArgs, через который можно остановить закрытие окна. Установка свойства e.Cancel = true предотвращает завершение и возвращает управление программе. Такой подход применяют, когда требуется дождаться записи данных, завершения фонового запроса или проверки состояния формы.
Перед отменой закрытия стоит определить причину, по которой окно пытаются завершить. Если инициатором является пользователь, можно отобразить диалог подтверждения или автоматически сохранить изменения. При закрытии из кода важно исключить циклы, возникающие при повторном вызове Close(). Для этого обычно используют флаг, фиксирующий состояние процесса закрытия.
При наличии длительных операций полезно запускать их асинхронно, чтобы не блокировать интерфейс. После завершения задач можно вызвать закрытие повторно, но только при отсутствии активных ограничений. Такой порядок позволяет избежать сбоев и сохраняет контроль над состоянием приложения в моменты выхода.
Завершение процесса при ошибках через DispatcherUnhandledException
Событие DispatcherUnhandledException вызывается при необработанных исключениях в основном потоке WPF. Оно позволяет перехватить ошибки до аварийного завершения и выполнить контролируемое закрытие приложения. Подписка осуществляется в App.xaml.cs через Application.Current.DispatcherUnhandledException.
Рекомендованная последовательность действий при перехвате исключения:
- Логирование ошибки для последующего анализа.
- Сохранение состояния данных или временных файлов.
- Оповещение пользователя о проблеме с возможностью завершить работу.
- Установка e.Handled = true, если требуется предотвратить аварийное закрытие, и вызов Application.Current.Shutdown() после завершения операций.
Использование DispatcherUnhandledException позволяет:
- Минимизировать потерю данных при сбоях.
- Контролировать время закрытия и выполнять очистку ресурсов.
- Разграничивать ошибки пользовательского интерфейса и фоновых потоков.
Важно помнить, что это событие срабатывает только для UI-потока. Для фоновых потоков и задач Task необходимо отдельно обрабатывать исключения через TaskScheduler.UnobservedTaskException или конструкции try/catch.
Закрытие приложения из фоновых задач или сервисов

При работе с фоновыми потоками или сервисами закрытие WPF-приложения через стандартные методы окон (Close()) невозможно, так как они не связаны напрямую с UI-потоком. В таких случаях используют Application.Current.Dispatcher.Invoke для выполнения действий в главном потоке или вызывают Application.Current.Shutdown() напрямую из фонового кода.
Рекомендации при закрытии из фоновых задач:
- Перед вызовом Shutdown убедиться, что все фоновые операции завершены или корректно остановлены.
- Использовать Dispatcher.Invoke для вызова методов интерфейса, если нужно обновить UI перед закрытием.
- Сохранять состояние данных и освобождать ресурсы, чтобы не оставлять подвисшие процессы или открытые соединения.
- Для задач, запускаемых через Task или Thread, предусматривать обработку исключений и логирование перед завершением.
Такой подход обеспечивает контролируемое завершение приложения даже при сложных фоновых сценариях и предотвращает потерю данных или блокировку ресурсов.
Вопрос-ответ:
Как правильно завершить работу WPF-приложения через код?
Для завершения приложения из кода используется метод Application.Current.Shutdown(). Он закрывает все окна и запускает событие Exit, что позволяет выполнить сохранение данных или очистку ресурсов. При необходимости можно передать код завершения, чтобы сигнализировать о штатном или аварийном закрытии.
В чём разница между Close() и Shutdown() для окон WPF?
Метод Close() закрывает только текущее окно и инициирует события Closing и Closed. Поведение всего приложения зависит от ShutdownMode. Метод Shutdown() завершает работу приложения полностью, независимо от количества открытых окон, и сразу запускает событие Exit.
Можно ли предотвратить закрытие окна при незавершённых операциях?
Да, для этого используется обработчик события Closing. Внутри него через объект CancelEventArgs устанавливают e.Cancel = true, если нужно остановить закрытие, например, для сохранения данных, завершения фоновых задач или отображения предупреждения пользователю.
Как закрыть WPF-приложение из фонового потока?
Фоновые потоки не имеют прямого доступа к UI. Для завершения приложения используют Application.Current.Dispatcher.Invoke для вызова методов главного потока или вызывают Application.Current.Shutdown() напрямую. Перед этим важно убедиться, что фоновые операции завершены и ресурсы освобождены.
Что делать при необработанных исключениях в WPF для корректного закрытия?
Событие DispatcherUnhandledException позволяет перехватить ошибки в основном потоке. В обработчике можно выполнить логирование, сохранить данные, уведомить пользователя и вызвать Application.Current.Shutdown() после завершения всех операций. Для фоновых потоков используют TaskScheduler.UnobservedTaskException.
