Gerir o estado da aplicação de forma eficaz

Numa aplicação SDK de Aplicações Windows de ambiente de trabalho, o seu processo continua a correr até que o utilizador o feche explicitamente. As aplicações de ambiente de trabalho não passam pelo ciclo automático de suspensão, retomada e terminação que as aplicações UWP usam. Isto dá-te mais controlo, mas também mais responsabilidade na gestão do estado.

Pré-requisitos

  • Um projeto de ambiente de trabalho do SDK de Aplicações Windows. Para os passos de configuração, consulte Criar a sua primeira aplicação WinUI 3.
  • Familiaridade com a classe Microsoft.UI.Windowing.AppWindow e, no caso de aplicações empacotadas, Windows.Storage.ApplicationData.

Importante

As aplicações de ambiente de trabalho do SDK de Aplicações Windows não são automaticamente suspensas nem retomadas como as aplicações UWP. A sua aplicação corre continuamente até que o utilizador ou o sistema a desliguem. Deve ainda implementar boas práticas de gestão do estado para lidar com desligamentos inesperados, reinicializações do sistema e falhas de energia.

Por que a gestão estatal é importante

Embora as aplicações de ambiente de trabalho não sejam suspensas pelo sistema operativo, ainda existem situações em que a sua aplicação pode perder o estado não guardado:

  • O utilizador fecha a aplicação enquanto o trabalho está em curso
  • O sistema reinicia para uma atualização
  • A perda de energia ou uma queda interrompe o processo
  • O utilizador faz logout do Windows

Desenhe a sua aplicação para gerir estes casos de forma eficiente, guardando frequentemente o estado importante e restaurando-o quando a aplicação inicia.

Guardar o estado incrementalmente

Em vez de guardar todo o estado ao encerrar, guarde o estado de forma incremental à medida que o utilizador trabalha. Isto reduz o risco de perda de dados e distribui o custo de I/O ao longo da vida útil da sua aplicação.

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>();
}

Use o ApplicationData para aplicações empacotadas

Se a sua aplicação estiver equipada com MSIX, pode usar Windows.Storage.ApplicationData.Current.LocalSettings para armazenar pequenas definições e LocalFolder para dados maiores. Estas localizações são geridas pelo sistema e limpas quando a aplicação é desinstalada.

Note

ApplicationData.Current requer identidade do pacote. Se a sua aplicação estiver desempacotada, use Environment.SpecialFolder.LocalApplicationData outra localização padrão de ficheiro em vez disso.

// 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;

Restaurar o estado ao iniciar

Quando a tua aplicação iniciar, verifica o estado guardado anteriormente e restaura-o. Isto proporciona ao utilizador uma experiência fluida — retoma de onde ficou.

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.
}

Processar o evento AppWindow.Closing

Utilize o evento Closing em Microsoft.UI.Windowing.AppWindow para guardar o estado crítico antes de a aplicação terminar. Esta é a tua última oportunidade de persistir no estado quando o utilizador fecha a janela.

Importante

No WinUI 3, o Window.Closed evento não suporta cancelamento. Para pedir confirmação ao utilizador antes de fechar (por exemplo, para guardar alterações não guardadas), utilize antes o evento Microsoft.UI.Windowing.AppWindow.Closing, que fornece um objeto AppWindowClosingEventArgs com a propriedade 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.
}

Para avisar o utilizador antes de fechar, defina args.Cancel = true para impedir que a janela feche e depois feche-a programaticamente após o utilizador confirmar. Use uma bandeira de guarda para evitar a reentrada, porque chamar this.Close() reativa o Closing evento:

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;
}

Melhores práticas

Practice Guidance
Gravar frequentemente Não espere pelo desligamento — guarde o estado enquanto o utilizador trabalha
Usa I/O assíncrona FileStream Use com useAsync: true ou File.WriteAllTextAsync para evitar bloquear a interface
Minimizar o tamanho do estado Guarda apenas os dados de que precisas para restaurar a posição do utilizador
Gerir eventos de energia Ouça para PowerManager.EnergySaverStatusChanged adaptar o comportamento
Testar desligamentos inesperados Use o Gestor de Tarefas para terminar a sua aplicação e verificar a recuperação do estado