Efektywne zarządzanie stanem aplikacji

W aplikacji Zestaw SDK do aplikacji systemu Windows klasycznej proces będzie kontynuowany do momentu jawnego zamknięcia go przez użytkownika. Aplikacje klasyczne nie przechodzą przez automatyczne wstrzymanie, wznowienie i zakończenie cyklu życia używanego przez aplikacje platformy UWP. Zapewnia to większą kontrolę, ale także większą odpowiedzialność za zarządzanie stanem.

Wymagania wstępne

  • Projekt aplikacji klasycznej w technologii Zestaw SDK do aplikacji systemu Windows. Aby uzyskać instrukcje konfiguracji, zobacz Tworzenie pierwszej aplikacji WinUI 3.
  • Znajomość klasy Microsoft.UI.Windowing.AppWindow oraz, w przypadku aplikacji spakietowanych, Windows.Storage.ApplicationData.

Ważna

Aplikacje klasyczne oparte na Zestaw SDK do aplikacji systemu Windows nie przechodzą automatycznie w stan zawieszenia i nie są automatycznie wznawiane, jak aplikacje UWP. Aplikacja działa w sposób ciągły, dopóki użytkownik lub system go nie zamknie. Należy nadal implementować dobre rozwiązania w zakresie zarządzania stanami, aby obsługiwać nieoczekiwane zamknięcia, ponowne uruchomienia systemu i utratę zasilania.

Dlaczego zarządzanie stanem ma znaczenie

Mimo że aplikacje klasyczne nie są zawieszone przez system operacyjny, nadal istnieją sytuacje, w których aplikacja może utracić niezapisany stan:

  • Użytkownik zamyka aplikację, gdy praca jest w toku
  • System uruchamia się ponownie na potrzeby aktualizacji
  • Utrata zasilania lub awaria kończy proces
  • Użytkownik wyloguje się z Windows

Zaprojektuj aplikację tak, aby obsługiwała te przypadki bezpiecznie, zapisując często ważny stan i przywracając go po uruchomieniu aplikacji.

Przyrostowe zapisywanie stanu

Zamiast zapisywać cały stan przy zamykaniu, zapisz stan przyrostowo, gdy użytkownik działa. Zmniejsza to ryzyko utraty danych i dystrybuuje koszt operacji we/wy w całym okresie istnienia aplikacji.

private async void OnThemeChanged(object sender, SelectionChangedEventArgs e)
{
    var selectedTheme = (sender as ComboBox)?.SelectedItem?.ToString();
    if (selectedTheme != null)
    {
        await SaveSettingAsync("AppTheme", selectedTheme);
    }
}

private async Task SaveSettingAsync(string key, string value)
{
    // Use local file storage, a database, or ApplicationData for packaged apps.
    var localFolder = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
    var settingsDir = System.IO.Path.Combine(localFolder, "MyApp");
    Directory.CreateDirectory(settingsDir);
    var settingsPath = System.IO.Path.Combine(settingsDir, "settings.json");

    // Read existing settings, update, and save asynchronously.
    var settings = LoadSettings(settingsPath);
    settings[key] = value;
    await File.WriteAllTextAsync(settingsPath, System.Text.Json.JsonSerializer.Serialize(settings));
}

private Dictionary<string, string> LoadSettings(string path)
{
    // Read and deserialize the settings file, or return an empty set if it doesn't exist yet.
    if (!File.Exists(path))
    {
        return new Dictionary<string, string>();
    }

    var json = File.ReadAllText(path);
    return System.Text.Json.JsonSerializer.Deserialize<Dictionary<string, string>>(json)
        ?? new Dictionary<string, string>();
}

Używanie usługi ApplicationData dla spakowanych aplikacji

Jeśli aplikacja jest spakowana przy użyciu pliku MSIX, możesz użyć Windows.Storage.ApplicationData.Current.LocalSettings do przechowywania małych ustawień i LocalFolder większych danych. Te lokalizacje są zarządzane przez system i czyszczone po odinstalowaniu aplikacji.

Note

ApplicationData.Current wymaga identyfikatora pakietu. Jeśli aplikacja jest bez pakietu, użyj Environment.SpecialFolder.LocalApplicationData lub innej standardowej lokalizacji plików.

// For packaged apps with package identity:
string filePath = "C:\\Users\\Example\\Documents\\report.docx";
var localSettings = Windows.Storage.ApplicationData.Current.LocalSettings;
localSettings.Values["LastOpenedFile"] = filePath;

Przywróć stan przy uruchamianiu

Po uruchomieniu aplikacji sprawdź wcześniej zapisany stan i przywróć go. Zapewnia to użytkownikowi płynne korzystanie — może kontynuować od miejsca, w którym skończył.

private void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
    // Restore the last opened file, if any.
    var localSettings = Windows.Storage.ApplicationData.Current.LocalSettings;
    if (localSettings.Values.TryGetValue("LastOpenedFile", out var path))
    {
        OpenFile(path.ToString());
    }
}

private void OpenFile(string path)
{
    // Load the document at the given path and update the UI.
}

Obsługa zdarzenia AppWindow.Closing

Użyj zdarzenia Closing w obiekcie Microsoft.UI.Windowing.AppWindow, aby zapisać krytyczny stan przed zakończeniem działania aplikacji. To ostatnia możliwość zachowania stanu, zanim użytkownik zamknie okno.

Ważna

W WinUI 3 zdarzenie Window.Closed nie obsługuje anulowania. Aby wyświetlić użytkownikowi monit przed zamknięciem (na przykład w celu zapisania niezapisanych zmian), zamiast tego użyj zdarzenia Microsoft.UI.Windowing.AppWindow.Closing, które udostępnia obiekt AppWindowClosingEventArgs z właściwością Cancel.

// In your Window constructor or initialization code:
var appWindow = this.AppWindow;
appWindow.Closing += AppWindow_Closing;

void AppWindow_Closing(AppWindow sender, AppWindowClosingEventArgs args)
{
    // See the following examples for what to do here.
}
private void AppWindow_Closing(AppWindow sender, AppWindowClosingEventArgs args)
{
    SaveCurrentDocument();
    SaveWindowPosition();
}

private void SaveCurrentDocument()
{
    // Persist the current document to disk.
}

private void SaveWindowPosition()
{
    // Persist the window's current size and position.
}

Aby monitować użytkownika przed zamknięciem, ustaw ustawienie args.Cancel = true , aby zapobiec zamykaniu okna, a następnie zamknij go programowo po potwierdzeniu przez użytkownika. Użyj flagi straży, aby zapobiec ponownej rejestracji, ponieważ wywołanie this.Close() ponownie wyzwala Closing zdarzenie:

private bool _isClosing = false;
private bool HasUnsavedChanges { get; set; }

private async void AppWindow_Closing(AppWindow sender, AppWindowClosingEventArgs args)
{
    if (_isClosing) return;

    if (HasUnsavedChanges)
    {
        args.Cancel = true; // Prevent the window from closing.

        // ShowSaveDialog returns true if the user chose to save.
        var shouldSave = await ShowSaveDialog();
        if (shouldSave)
        {
            await SaveCurrentDocumentAsync();
        }

        // Set the guard flag before closing to prevent re-entrancy.
        _isClosing = true;
        this.Close();
    }
}

private Task<bool> ShowSaveDialog()
{
    // Prompt the user to save, discard, or cancel.
    return Task.FromResult(true);
}

private Task SaveCurrentDocumentAsync()
{
    // Persist the current document to disk asynchronously.
    return Task.CompletedTask;
}

Najlepsze praktyki

Practice Guidance
Zapisuj często Nie czekaj na zamknięcie — zapisz stan, gdy użytkownik działa
Użyj asynchronicznego we/wy Użyj polecenia FileStream lub useAsync: trueFile.WriteAllTextAsync, aby uniknąć blokowania interfejsu użytkownika
Minimalizuj rozmiar stanu Zapisz tylko dane potrzebne do przywrócenia pozycji użytkownika
Obsługa zdarzeń zasilania Nasłuchuj PowerManager.EnergySaverStatusChanged, aby dostosować działanie
Przetestuj nieoczekiwane zamknięcia Użyj Menedżera zadań, aby zakończyć działanie aplikacji i zweryfikować odzyskiwanie stanu