Aplikacja WPF nie wyłącza się podczas zamykania okna głównego

82

Jestem przyzwyczajony do programowania WinForms w Visual Studio, ale chciałem spróbować WPF.

Dodałem do mojego projektu kolejne okno o nazwie Window01. Główne okno nazywa się MainWindow. Przed public MainWindow()konstruktorem deklaruję Window01:

Window01 w1;

Teraz tworzę wystąpienie tego okna w:

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

Mam przycisk gdzie okno jest pokazany: w1.ShowDialog();.

`` Zabawne '' jest to, że jeśli uruchomię aplikację (z debugowaniem) i wyjdę z niej kilka sekund później (nic nie robię w aplikacji), Visual Studio nie zatrzymuje debugowania tak, jakby aplikacja była nadal biegnie.

Jeśli przeniosę linię w1 = new Window01();do metody kliknięcia przycisku, czyli tuż powyżejShowDialog() , program Visual Studio zachowuje się poprawnie - to znaczy debugowanie zatrzymuje się po wyjściu z aplikacji.

Dlaczego to dziwne zachowanie?

gosr
źródło

Odpowiedzi:

138

W swoim MainWindow.xaml.cs, spróbuj zrobić to:

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

    Application.Current.Shutdown();
}

Za pomocą tego linku możesz również ustawić ShutdownModew języku 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>

Aplikacje przestają działać tylko wtedy, gdy wywoływana jest Shutdownmetoda Application. Zamknięcie może nastąpić niejawnie lub jawnie, zgodnie z wartością ShutdownModewłaściwości.

Jeśli ustawisz ShutdownModena OnLastWindowClose, Windows Presentation Foundation (WPF) niejawnie wywołuje Shutdown po zamknięciu ostatniego okna w aplikacji, nawet jeśli jakiekolwiek aktualnie utworzone okna są ustawione jako okno główne (zobacz MainWindow).

A ShutdownModez OnMainWindowClosepowoduje, że WPF niejawnie wywołuje Shutdown po zamknięciu MainWindow, nawet jeśli inne okna są obecnie otwarte.

Czas życia niektórych aplikacji może nie zależeć od tego, kiedy główne lub ostatnie okno jest zamknięte, lub może w ogóle nie zależeć od okien. W tych scenariuszach należy ustawić ShutdownModewłaściwość na OnExplicitShutdown, która wymaga jawnego Shutdownwywołania metody, aby zatrzymać aplikację. W przeciwnym razie aplikacja będzie nadal działać w tle.

ShutdownMode można skonfigurować deklaratywnie z XAML lub programowo z kodu.

Ta właściwość jest dostępna tylko w wątku, który utworzył Applicationobiekt.


W Twoim przypadku aplikacja nie zamyka się, ponieważ prawdopodobnie używasz domyślnej OnLastWindowClose:

Jeśli ustawisz ShutdownModena OnLastWindowClose, WPF niejawnie wywoła Shutdown po zamknięciu ostatniego okna w aplikacji, nawet jeśli jakiekolwiek aktualnie utworzone okna są ustawione jako okno główne (zobacz MainWindow).

Ponieważ otwierasz nowe okno, a nie je zamykasz, wyłączenie nie jest wywoływane.

Bob Horn
źródło
1
Należy zauważyć, że funkcja nadpisywania jest najlepsza, jeśli utworzono wystąpienie jakichkolwiek innych wątków w celu wykonania określonych zadań. Zatrzymaj wątki w funkcji OnClosed przed zamknięciem. W przeciwnym razie wątki pozostają żywe, a aplikacja nigdy nie zostanie naprawdę zamknięta.
weezma2004
Nie jestem pewien, czy zamknięcie aplikacji jest dobrym pomysłem, tylko dlatego, że MainWindow jest zamknięte. Możliwe jest również, że proces w tle próbuje zrobić coś takiego, jak zapisywanie w DB, zapisywanie w dziennikach, buforowanie rzeczy, odłączanie urządzeń itp. W tym momencie znalazłbym problem, który blokuje zamknięcie aplikacji i go naprawił. Zawsze organizuj cykl życia aplikacji. Application.Current.Shutdown()spowoduje natychmiastowe zamknięcie aplikacji, podobnie jak zakończenie procesu na pasku zadań. To jak strzelanie do kogoś, zanim powie ostatnie słowo :) .. Nie polecam.
Vural
35

Cieszę się, że dostałeś odpowiedź, ale dla dobra innych odpowiem również na twoje pytanie, aby dodać trochę informacji.


Krok 1

Po pierwsze, jeśli chcesz, aby program kończył pracę po zamknięciu okna głównego, musisz to określić, ponieważ nie jest to WinForms, w którym to zachowanie jest domyślne.

(Domyślnie w WPF jest zamknięcie ostatniego okna)

W kodzie

Przejdź do wystąpienia aplikacji w punkcie wejścia (w programie WPF VS 2012 wartość domyślna jest zagnieżdżona w środku App.xaml, więc przejdź do niego i przejdź do App.xaml.csi utwórz konstruktor).

W konstruktorze określić, że Application„s ShutdownModepowinno być ShutdownMode. OnLastWindowClose.

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

W XAML

Przejdź do App.xamlpliku, który VS 2012 domyślnie utworzone (lub utwórz go samodzielnie) Korzeń jest Applicationokreślić wewnątrz, że Application„s ShutdownModepowinno być 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">

Jeśli to zadziała, gotowe; możesz przestać czytać.


Krok 2

Jeśli powyższe nie zadziałało (myślę, że napisałeś aplikację WPF od zera), główne okno prawdopodobnie nie jest znane aplikacji jako główne. Więc to też określ.

W kodzie

Przejdź do konstruktora aplikacji, tak jak w kroku 1, i określ to Application. MainWindowwartość to Window:

MainWindow = mainWindow;

W XAML

Przejdź do Application kodu XAML, tak jak w kroku 1, i określ to Application. MainWindowwartość to Window:

MainWindow = "mainWindow";

Alternatywny

Nie sądzę, że jest to najlepsze podejście, tylko dlatego, że WPF nie chce, aby to zrobić (a więc ma Application„s ShutdownMode), ale można po prostu użyć zdarzenie / nadpisać metodę zdarzeń (OnEventHappened).

Przejdź do pliku związanego z kodem MainWindow i dodaj:

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

        App.Current.Shutdown();
    }
MasterMastic
źródło
3
Twoja odpowiedź jest lepsza niż ta, która jest zaznaczona.
Seekeer
Zwykle robię to w ten sposób: Ustaw MainWindowna wystąpienie okna głównego, ustaw ShutdownModena ShutdownMode.OnMainWindowClose, wywołaj MainWindow.Show()oddzwonienie do Startupzdarzenia App. Jest to najbardziej minimalne rozwiązanie, które wymyśliłem, które działa zgodnie z oczekiwaniami przynajmniej w .NET Core 3 z WPF.
TorbenJ
11

Ponieważ domyślny tryb zamykania w aplikacji WPF jest OnLastWindowClose, co oznacza, że ​​aplikacja zatrzymuje się po zamknięciu ostatniego okna.

Kiedy tworzysz wystąpienie nowego obiektu Window, jest on automatycznie dodawany do pliku listy okien w aplikacji. Tak więc problem polegał na tym, że aplikacja tworzyła dwa okna podczas uruchamiania - MainWindow i jeszcze nie pokazane Window01 - i gdybyś zamknął tylko MainWindow, Window01 utrzymywałby twoją aplikację w ruchu.

Zwykle tworzysz obiekt okna tą samą metodą, która wywołuje jego ShowDialog, i tworzysz nowy obiekt okna za każdym razem, gdy jest wyświetlane okno dialogowe.

Joe Daley
źródło
2

Natknąłem się na to pytanie, szukając czegoś innego i byłem zaskoczony, że nie widziałem żadnej z proponowanych odpowiedzi, o których mowa Window.Owner.

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

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

Wywołanie Window.GetWindow(this)jest bardzo przydatna z punktu widzenia aplikacji MVVM, gdy jesteś daleko w dół drzewa wizualnej nie wiedząc, gdzie zostałeś instancji, może być wywołana przez dostarczenie jakiegokolwiek FrameWorkelementu (np UserContol, Button, Page). Oczywiście, jeśli masz bezpośrednie odniesienie do okna, użyj tego lub nawetApplication.Current.MainWindow .

Jest to dość potężna relacja, która ma wiele przydatnych korzyści, z których możesz nie zdawać sobie sprawy (zakładając, że nie zakodowałeś osobnych okien, aby uniknąć tych relacji).

Jeśli nazwiemy Twoje główne okno MainWindowi drugie okno tak jak AdditionalWindowwtedy ....

  1. Minimalizacja MainWindowwoli również zminimalizowaćAdditionalWindow
  2. Przywrócenie MainWindowrównież przywróciAdditionalWindow
  3. Zamknięcie MainWindowzostanie zamknięte AdditionalWindow, ale zamknięcie AdditionalWindownie zostanie zamknięteMainWindow
  4. AdditioanlWindownigdy się nie „zgubi” pod MainWindow, tzn. AddditionalWindowzawsze wyświetla się powyżej MainWindoww kolejności z, jeśli był wcześniej Show()wyświetlany (całkiem przydatne!)

Należy jednak zauważyć, że jeśli masz taką relację, Closingzdarzenie w AdditionalWindowelemencie nie jest wywoływane, więc musisz ręcznie iterować po OwnedWindowskolekcji. np. Stwórz sposób wywoływania każdego okna za pomocą nowego interfejsu lub metody klasy bazowej.

    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();
        }        
    }
Rhys
źródło
1

Żadne z powyższych nie zadziałało dla mnie, może dlatego, że nasz projekt wykorzystuje Prism. Skończyło się na użyciu tego w App.XAML.cs

    protected override void OnExit(ExitEventArgs e)
    {
        base.OnExit(e);
        Process.GetCurrentProcess().Kill();
    }
sadis
źródło
0

Wygląda na coś, na co natknąłem się, gdy utworzyłem drugie okno, które pełniło rolę okna dialogowego. Gdy drugie okno zostało otwarte, a następnie zamknięte, a następnie główne okno zostało zamknięte, aplikacja nadal działała (w tle). Dodałem (lub potwierdziłem) następujące elementy w moim App.xaml:

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

Brak przyjemności.

W końcu przeszedłem do mojego „MainWindow.xaml” i dodałem właściwość „Closed” do okna, która przeszła do metody „MainWind_Closed”, która wygląda następująco:

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

Po uruchomieniu debugera wygląda na to, że jedynym oknem, które się pojawia, jest okno, które utworzyłem jako okno dialogowe - innymi słowy, pętla foreach znajduje tylko jedno okno - okno dialogowe, a nie główne.

Miałem "this.Close ()" uruchomioną w metodzie, która zamknęła okno dialogowe i miałem "dlgwin.Close ()", który pojawił się po "dlgwin.ShowDialog ()" i to nie zadziałało. Ani nawet "dlgwin = null".

Dlaczego więc to okno dialogowe nie zamknęło się bez tych dodatkowych rzeczy? No cóż. To działa.

Lowell
źródło