在桌面版 Windows 應用程式 SDK 應用程式中,你的程序會持續執行,直到使用者明確關閉它為止。 桌面應用程式不會像 UWP 應用程式那樣自動暫停、恢復和終止生命週期。 這不僅讓你有更多控制權,也更有管理州的責任。
先決條件
- 一個 Windows 應用程式 SDK 桌面專案。 關於設定步驟,請參見 「建立你的第一個 WinUI 3 應用程式」。
- 熟悉
Microsoft.UI.Windowing.AppWindow類別,以及若為封裝應用程式,則熟悉Windows.Storage.ApplicationData。
Important
桌面版 Windows 應用程式 SDK 應用程式不會像 UWP 應用程式那樣自動暫停並恢復。 你的應用程式會持續運行,直到使用者或系統關閉為止。 你仍應實施良好的狀態管理措施,以應對意外關機、系統重啟和斷電。
為什麼國家管理很重要
即使桌面應用程式不會被作業系統暫停,仍有一些情況下你的應用程式可能會失去未儲存的狀態:
- 使用者在工作進行時關閉應用程式
- 系統會重新啟動以進行更新
- 停電或當機會終止該處理程序
- 使用者從 Windows 登出
設計你的應用程式能優雅地處理這些情況,經常儲存重要狀態,並在應用程式啟動時還原。
逐步儲存狀態
與其在關機時儲存所有狀態,不如隨著使用者操作逐步儲存狀態。 這降低了資料遺失的風險,並將 I/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>();
}
使用 ApplicationData 來處理打包應用程式
如果你的應用程式是用 MSIX 包裝,可以用 Windows.Storage.ApplicationData.Current.LocalSettings 來儲存小型設定和 LocalFolder 較大的資料。 這些位置由系統管理,並在應用程式卸載後清理。
Note
ApplicationData.Current 需要套件識別。 如果您的應用程式未封裝,請改用 Environment.SpecialFolder.LocalApplicationData 或其他標準檔案位置。
// 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;
啟動時還原狀態
當你的應用程式啟動時,檢查之前有沒有儲存過的狀態並還原它。 這讓使用者體驗流暢——他們能從中斷的地方繼續。
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.
}
處理 AppWindow.Closing 事件
使用 Microsoft.UI.Windowing.AppWindow 上的 Closing 事件,在應用程式終止前儲存關鍵狀態。 這是使用者關閉視窗時,你最後一次持續維持狀態的機會。
Important
在 WinUI 3 中,活動 Window.Closed 不支援取消。 若要在關閉前提示使用者(例如儲存未儲存的變更),請使用 Microsoft.UI.Windowing.AppWindow.Closing 事件,該事件會提供 AppWindowClosingEventArgs 帶有 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.
}
若要在關閉前提示使用者,請設定 args.Cancel = true 為防止視窗關閉,然後在使用者確認後以程式方式關閉視窗。 使用保護旗標以防止重複進入,因為呼叫 this.Close() 會 Closing 重新觸發事件:
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;
}
最佳做法
| 練習 | 指導 |
|---|---|
| 經常存檔 | 不要等關機——在使用者運作時儲存狀態 |
| 使用非同步輸入輸出 | 使用 FileStream 搭配 useAsync: true 或 File.WriteAllTextAsync,以避免阻塞使用者介面 |
| 最小化州份大小 | 只儲存恢復使用者位置所需的資料 |
| 處理電源事件 | 監聽 PowerManager.EnergySaverStatusChanged 以調整行為 |
| 測試意外關機 | 使用工作管理員終止應用程式並驗證狀態恢復 |