Приложение WPF не закрывается при закрытии главного окна

82

Я привык к программированию WinForms в Visual Studio, но я хотел попробовать WPF.

Я добавил в свой проект еще одно окно под названием Window01. Главное окно называется MainWindow. Перед public MainWindow()конструктором объявляю Window01:

Window01 w1;

Теперь я создаю экземпляр этого окна в:

private void Window_Loaded(object sender, RoutedEventArgs e)
{
    w1 = new Window01();            
}

У меня есть кнопка , где отображается окно: w1.ShowDialog();.

«Забавным» здесь является тот факт, что если я запускаю приложение (с отладкой) и выхожу из него через несколько секунд (я ничего не делаю в приложении), Visual Studio не прекращает отладку, как если бы приложение Все еще работает.

Если я перемещаю строку w1 = new Window01();в метод нажатия кнопки, то есть чуть выше ShowDialog(), Visual Studio ведет себя правильно, то есть отладка останавливается, когда я выхожу из приложения.

Почему такое странное поведение?

госр
источник

Ответы:

138

В вашем MainWindow.xaml.csпопробуйте сделать это:

protected override void OnClosed(EventArgs e)
{
    base.OnClosed(e);

    Application.Current.Shutdown();
}

По этой ссылке вы также можете установить ShutdownModeв XAML:

http://msdn.microsoft.com/en-us/library/system.windows.application.shutdownmode.aspx

<Application
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    StartupUri="MainWindow.xaml"
    ShutdownMode="OnExplicitShutdown"
    >
</Application>

Приложения перестают работать только при вызове Shutdownметода Application. Завершение работы может происходить неявно или явно, в зависимости от значения ShutdownModeсвойства.

Если установлено ShutdownModeзначение OnLastWindowClose, Windows Presentation Foundation (WPF) неявно вызывает Shutdown при закрытии последнего окна в приложении, даже если какие-либо окна, созданные в данный момент, установлены в качестве главного окна (см. MainWindow).

По одной ShutdownModeиз OnMainWindowCloseпричин WPF неявно вызывает Shutdown при закрытии MainWindow, даже если в данный момент открыты другие окна.

Время жизни некоторых приложений может не зависеть от того, когда было закрыто главное или последнее окно, или вообще не зависеть от окон. Для этих сценариев вам необходимо установить для ShutdownModeсвойства значение OnExplicitShutdown, что требует явного Shutdownвызова метода для остановки приложения. В противном случае приложение продолжит работу в фоновом режиме.

ShutdownMode можно настроить декларативно из XAML или программно из кода.

Это свойство доступно только из потока, создавшего Applicationобъект.


В вашем случае приложение не закрывается, потому что вы, вероятно, используете значение по умолчанию OnLastWindowClose:

Если установлено ShutdownModeзначение OnLastWindowClose, WPF неявно вызывает Shutdown при закрытии последнего окна в приложении, даже если какие-либо окна, созданные в данный момент, установлены в качестве главного окна (см MainWindow. Раздел "Ресурсы" ).

Поскольку вы открываете новое окно, но не закрываете его, выключение не вызывается.

Боб Хорн
источник
1
Важно отметить, что функция переопределения лучше всего подходит, если вы создали экземпляры любых других потоков для выполнения определенных задач. Остановите потоки в функции OnClosed перед завершением работы. В противном случае потоки останутся в живых, и приложение никогда не завершит работу.
weezma2004
Я не уверен, стоит ли закрыть приложение только потому, что MainWindow закрыто. Также возможно, что этот фоновый процесс пытается сделать что-то вроде сохранения в БД, записи в журналы, кеширования, отключения устройств и т. Д. На этом этапе я обнаружил бы проблему, которая блокирует закрытие приложения, и исправлю ее. Всегда организуйте жизненный цикл своих приложений. Application.Current.Shutdown()немедленно завершит работу приложения, как завершение процесса на панели задач. Это как выстрелить в кого-то, прежде чем он скажет свое последнее слово :) .. Я бы не рекомендовал.
Vural
35

Я рад, что вы получили свой ответ, но ради других я отвечу и на ваш вопрос, чтобы добавить некоторую информацию.


Шаг 1

Во-первых, если вы хотите, чтобы ваша программа закрывалась при закрытии главного окна, вам нужно указать, поскольку это не WinForms, где это поведение по умолчанию.

(По умолчанию в WPF закрывается последнее окно)

В коде

Перейдите к экземпляру приложения в точке входа (в программе WPF VS 2012 значение по умолчанию вложено внутрь App.xaml, поэтому войдите в него и перейдите к конструктору App.xaml.csи создайте его).

В конструкторе указать , что ваши Application«s ShutdownModeдолжно быть ShutdownMode. OnLastWindowClose.

    public App()
    {
        ShutdownMode = ShutdownMode.OnLastWindowClose;
    }

В XAML

Перейдите к App.xamlфайлу , который VS 2012 , созданный по умолчанию (или создать его самостоятельно) Корень является Applicationуказать внутри , что ваши Application«s ShutdownModeдолжно быть ShutdownMode. OnLastWindowClose.

<Application x:Class="WpfApplication27.App"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         StartupUri="MainWindow.xaml"
         ShutdownMode="OnMainWindowClose">

Если это работает, все готово; можешь перестать читать.


Шаг 2

Если вышеуказанное не сработало (я думаю, вы написали приложение WPF с нуля), главное окно, вероятно, не известно приложению как главное окно. Так что укажите и это.

В коде

Перейдите в конструктор приложения, как вы делали на шаге 1, и укажите это Application. MainWindowценность - это ваше Window:

MainWindow = mainWindow;

В XAML

Перейдите к ApplicationXAML, как вы делали на шаге 1, и укажите это Application. MainWindowценность - это ваше Window:

MainWindow = "mainWindow";

Альтернатива

Я не думаю , что это лучший подход, просто потому , что WPF не хочет , чтобы вы это сделать (так он имеет Application«s ShutdownMode), но вы можете просто использовать событие / переопределить метод события (OnEventHappened).

Перейдите в файл кода программной части MainWindow и добавьте:

    protected override void OnClosed(EventArgs e)
    {
        base.OnClosed(e);

        App.Current.Shutdown();
    }
МастерМастик
источник
3
Ваш ответ лучше, чем отмеченный.
Seekeer
Обычно я делаю это так: устанавливаю MainWindowэкземпляр главного окна, устанавливаю ShutdownModeзначение ShutdownMode.OnMainWindowClose, вызываю MainWindow.Show()обратный вызов для Startupсобытия App. Это самое минимальное решение, которое я придумал, которое работает должным образом, по крайней мере, в .NET Core 3 с WPF.
TorbenJ
11

Поскольку режим завершения работы по умолчанию в приложении WPF - OnLastWindowClose, это означает, что приложение останавливается, когда закрывается последнее окно.

Когда вы создаете экземпляр нового объекта Window, он автоматически добавляется в список окон в приложении. Итак, проблема заключалась в том, что ваше приложение при запуске создавало два окна - MainWindow и еще не показанное Window01 - и если вы закроете только MainWindow, Window01 продолжит работу вашего приложения.

Обычно вы создаете объект окна в том же методе, который будет вызывать его ShowDialog, и вы будете создавать новый объект окна каждый раз, когда отображается диалог.

Джо Дейли
источник
2

Я наткнулся на этот вопрос, когда искал что-то еще, и был удивлен, что не увидел ни одного из предложенных ответов, упоминающих о Window.Owner.

    {
       var newWindow = new AdditionalWindow();
       newWindow.Owner = Window.GetWindow(this);

       // then later on show the window with Show() or ShowDialog()
    }

Вызов Window.GetWindow(this)является весьма полезным с точки зрения приложения MVVM , когда вы далеко вниз по визуальному дереву , не зная , где ты был создан экземпляр, он может быть вызван путем подачи любого FrameWorkэлемента (например UserContol, Button, Page). Очевидно, если у вас есть прямая ссылка на окно, используйте это или даже Application.Current.MainWindow.

Это довольно мощная взаимосвязь, которая имеет ряд полезных преимуществ, которые вы могли не осознавать с самого начала (при условии, что вы специально не закодировали отдельные окна, чтобы избежать этих взаимосвязей).

Если мы назовем ваше главное окно MainWindowи второе окно как AdditionalWindowthen ....

  1. Сведение к минимуму MainWindowволи также минимизируетAdditionalWindow
  2. Восстановление MainWindowтакже восстановитAdditionalWindow
  3. Закрытие MainWindowзакроется AdditionalWindow, но закрытие AdditionalWindowне закроетсяMainWindow
  4. AdditioanlWindowникогда не потеряется внизу MainWindow, т.е. AddditionalWindowвсегда отображается вверху MainWindowв z-порядке, если вы использовали его Show()для отображения (весьма полезно!)

Однако следует отметить одну вещь: если у вас есть эта связь, то Closingсобытие в AdditionalWindowне вызывается, поэтому вам придется вручную перебирать OwnedWindowsколлекцию. например, создать способ вызова каждого окна через новый интерфейс или метод базового класса.

    private void MainWindow_OnClosing(object sender, CancelEventArgs e)
    {
        foreach (var window in OwnedWindows)
        {
            var win = window as ICanCancelClosing;  // a new interface you have to create
            e.Cancel |= win.DoYouWantToCancelClosing();
        }        
    }
Рис
источник
1

Ничего из вышеперечисленного у меня не сработало, возможно, потому, что в нашем проекте используется Prism. Итак, в итоге я использовал это в App.XAML.cs

    protected override void OnExit(ExitEventArgs e)
    {
        base.OnExit(e);
        Process.GetCurrentProcess().Kill();
    }
Садис
источник
0

Похоже, я столкнулся с чем-то, когда создавал второе окно, действующее как диалоговое окно. Когда второе окно было открыто, а затем закрыто, а затем закрылось главное окно, приложение продолжало работать (в фоновом режиме). Я добавил (или подтвердил) следующее в свой App.xaml:

<Application x:Class="XXXXXXX.App"
             ...  
             StartupUri="MainWindow.xaml"
             ShutdownMode="OnMainWindowClose">
    <Application.MainWindow >
        <NavigationWindow Source="MainWindow.xaml" Visibility="Visible" />
    </Application.MainWindow>

Никакой радости.

Итак, я наконец зашел в свой «MainWindow.xaml» и добавил свойство «Closed» к окну, которое перешло к методу «MainWind_Closed», который выглядит следующим образом:

 private void MainWind_Closed(object sender, EventArgs e)
        {
            foreach ( Window w in App.Current.Windows )
            {
                if (w.DataContext != this)
                    w.Close();
            }
        }

Проходя через отладчик, похоже, что единственное окно, которое появляется, - это окно, которое я создал как диалог - другими словами, цикл foreach находит только одно окно - диалог, а не главное.

У меня был «this.Close ()», работающий в методе, закрывающем диалог, и «dlgwin.Close ()», который шел после «dlgwin.ShowDialog ()», и это не сработало. Даже "dlgwin = null".

Итак, почему бы этот диалог не закрыть без этого дополнительного материала? Ну что ж. Это работает.

Лоуэлл
источник