UWP 已不再積極開發。 WinUI 3 和 Windows 應用程式 SDK 是它的後繼者——而 AI 工具可以自動化大部分的遷移工作。 主要挑戰是 AI 模型是用多年 UWP 樣本訓練的,沒有指導,它們會重現你想擺脫的模式。 這個頁面會提供你的代理程式所需的脈絡資訊,讓它能正確處理。
安裝 WinUI 代理插件
該 winui-uwp-migration 技能會自動處理常見的替換:
gh copilot plugin install winui@awesome-copilot
完整細節請參見 WinUI 代理插件 。
API 替換表
以下表格總結了最常見的 API 替換方式。 完整詳細的映射——包括成員、屬性及較少見的 API——請參見「將 UWP API 與函式庫映射至 Windows 應用程式 SDK」。
Important
x:Bind 預設為 OneTime 模式。 與(預設為 {Binding})OneWay不同x:Bind,除非你指定Mode=OneWay或Mode=TwoWay,否則只會評估一次。 遷移時,審核所有 x:Bind 綁定到執行時會變動屬性的表達式——缺少 Mode=OneWay 會導致「UI 不更新」的錯誤,而這些錯誤在編譯時是看不見的。
Namespaces
| UWP | WinUI 3 |
|---|---|
Windows.UI.Xaml.* |
Microsoft.UI.Xaml.* |
Windows.UI.Xaml.Controls.* |
Microsoft.UI.Xaml.Controls.* |
Windows.UI.Xaml.Media.* |
Microsoft.UI.Xaml.Media.* |
Windows.UI.Composition |
Microsoft.UI.Composition |
線程
| UWP | WinUI 3 |
|---|---|
CoreDispatcher |
DispatcherQueue |
Dispatcher.RunAsync(...) |
DispatcherQueue.TryEnqueue(...) |
CoreApplication.MainView.CoreWindow.Dispatcher |
this.DispatcherQueue (來自 a Window 或 Page) |
Windowing
| UWP | WinUI 3 |
|---|---|
ApplicationView |
AppWindow |
ApplicationView.GetForCurrentView() |
AppWindow.GetFromWindowId(...) |
ApplicationViewTitleBar |
AppWindowTitleBar |
CoreWindow |
Microsoft.UI.Xaml.Window |
SystemNavigationManager |
透過 AppWindowTitleBar 返回鍵 |
對話與選擇器
| UWP | WinUI 3 |
|---|---|
MessageDialog |
ContentDialog(設定 XamlRoot) |
FileOpenPicker |
FileOpenPicker + InitializeWithWindow |
FileSavePicker |
FileSavePicker + InitializeWithWindow |
FolderPicker |
FolderPicker + InitializeWithWindow |
Important
選擇器在呼叫InitializeWithWindow(或類似函式)之前需要PickSingleFileAsync:
var hwnd = WinRT.Interop.WindowNative.GetWindowHandle(App.MainWindow);
WinRT.Interop.InitializeWithWindow.Initialize(picker, hwnd);
ContentDialog 需要 XamlRoot (非 InitializeWithWindow):
var dialog = new ContentDialog { XamlRoot = this.Content.XamlRoot, ... };
await dialog.ShowAsync();
Notifications
| UWP | WinUI 3 |
|---|---|
Windows.UI.Notifications.ToastNotificationManager |
Microsoft.Windows.AppNotifications.AppNotificationManager |
Windows.UI.Notifications.BadgeUpdateManager |
Microsoft.Windows.BadgeNotifications.BadgeNotificationManager |
Windows.UI.Notifications.TileUpdateManager |
磁磚已被棄用——請使用通知或小工具 |
網路與 HTTP
| UWP | WinUI 3(推薦) |
|---|---|
Windows.Web.Http.HttpClient |
System.Net.Http.HttpClient (攜帶型,無 WinRT 依賴) |
Windows.Web.Syndication.SyndicationClient |
System.ServiceModel.Syndication.SyndicationFeed + HttpClient |
Windows.Web.AtomPub.AtomPubClient |
System.ServiceModel.Syndication 或直接 HTTP 連線 |
Note
WinRT 的 HTTP API (Windows.Web.Http) 仍然能在封裝的 WinUI 3 應用程式中使用,但建議使用 .NET 等效物,以提升可攜性、更簡單的除錯,以及更廣泛的生態系統支援(中介軟體、DI、模擬)。
導航
| UWP | WinUI 3 |
|---|---|
Frame.Navigate(typeof(MyPage)) |
Frame.Navigate(typeof(MyPage)) — 未曾改變 |
SystemNavigationManager.BackRequested |
透過 NavigationView 或 AppWindow 處理 |
Windows.UI.Core.Preview.SystemNavigationManagerPreview |
AppWindow.Closing 事件 |
Note
自訂漢堡式導覽(SplitView + NavMenuListView):許多 UWP 範例使用自訂的 AppShell.xaml 搭配 SplitView,以及自行撰寫的 NavMenuListView 控制項(約 500 多行)來實作導覽。 在 WinUI 3 中,將整個模式替換為 NavigationView,提供相同的使用者體驗,並內建無障礙功能、響應式行為及返回按鈕支援。 這通常是 80% 的程式碼縮減。
MVVM 模式
| UWP(常見自訂實作) | WinUI 3(推薦) |
|---|---|
自訂 BindableBase / ObservableObject |
CommunityToolkit.Mvvm.ComponentModel.ObservableObject |
自訂 DelegateCommand / RelayCommand |
CommunityToolkit.Mvvm.Input.RelayCommand |
手動 SetProperty + OnPropertyChanged |
[ObservableProperty] 源產生器 |
自訂 INavigationService |
內建 Frame.Navigate + NavigationView |
Tip
CommunityToolkit.Mvvm NuGet 套件是 WinUI 3 應用程式推薦的 MVVM 基礎。 它以經過測試、由原始碼產生的對應實作取代手寫的基底類別,省去數百行樣板程式碼。
dotnet add package CommunityToolkit.Mvvm
應用程式週期
| UWP | WinUI 3 |
|---|---|
Application.Current.Suspending |
Microsoft.Windows.AppLifecycle(需架構變更 — 詳見註解) |
Application.Current.Resuming |
AppInstance.GetCurrent().Activated (見註解) |
BackgroundTaskBuilder |
Windows 應用程式 SDK背景任務 |
Note
WinUI 3 應用程式生命週期遷移並非簡單的 API 名稱交換。 Windows 應用程式 SDK 採用不同的啟用與暫停模式。 將生命週期程式碼視為需要專用重寫,而非自動替換。 完整模型請參閱 Windows 應用程式 SDK 生命週期文件。
設定與儲存
| UWP | WinUI 3(打包版) | WinUI 3(未封裝) |
|---|---|---|
ApplicationData.Current.LocalSettings |
未更改 | ❌ 拋出 — 無封裝身份 |
ApplicationData.Current.LocalFolder |
未更改 | ❌ 拋出 — 無封裝身份 |
Windows.Storage.KnownFolders |
未更改 | ❌ 拋出 — 無封裝身份 |
Warning
未封裝的應用程式 無法使用 ApplicationData.Current ——因為沒有套件身份,執行時會丟出。 改用標準的 .NET 檔案 API:
var appData = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
"YourAppName");
Directory.CreateDirectory(appData);
var json = JsonSerializer.Serialize(data);
await File.WriteAllTextAsync(Path.Combine(appData, "settings.json"), json);
Note
如果你的 UWP 應用程式曾使用 DataContractSerializer[DataMember]/[IgnoreDataMember],考慮遷移到System.Text.Json(更快、規模更小、原始碼生成支援)。 屬性映射為:
-
[DataMember]→[JsonPropertyName("name")](或直接使用房產名稱) -
[IgnoreDataMember]→[JsonIgnore] -
[DataContract]→ 不需要等效的(System.Text.Json 預設會序列化公開屬性)
不變的 API
Windows.Devices.*、Windows.Media.*、Windows.UI.ViewManagement.UISettings、Windows.UI.Color,以及大多數 XAML 命名空間外的 WinRT API 均未改變。
無直接對應的控制項
有些 UWP 控制項在 WinUI 3 裡並不存在。 根據你的情況選擇替換方案:
| UWP 控制項 | WinUI 3 替代方案 | 註釋 |
|---|---|---|
Pivot |
TabView、NavigationView(頂端模式)或 RadioButtons + 可見性 |
對於 2–3 個固定分頁,使用 RadioButtons 搭配可見性切換最簡單。 對於動態/可關閉的分頁,請使用 TabView。 |
RadialController |
WinRT 與視窗柄的互通 |
RadialController.CreateForCurrentView() 沒有直接對應項目。
RadialControllerInterop 與 GetForWindow(hwnd) 一起使用。 |
SystemNavigationManager |
自訂返回按鈕或 NavigationView.IsBackEnabled |
SystemNavigationManager.GetForCurrentView() 不存在。 新增自己的返回按鈕,或使用 NavigationView內建的返回按鈕。 |
墨水、Win2D 與印刷
這些子系統需要特定的遷移步驟,超越命名空間的變更。
Windows Ink
WinUI 包含 InkCanvas、 InkToolbar、 InkPresenter 作為 Windows 應用程式 SDK 實驗版本中的實驗性 API。 穩定版不包含這些項目。 關於設定、工具列自訂及已知限制,請參見 WinUI 應用程式的手寫筆跡控制項。
在實驗性版本中,控制項和處理墨水輸入的類型位於 Microsoft.UI.Xaml.Controls 中。 筆劃與繪畫屬性保留於 Windows.UI.Input.Inking:
| UWP | WinUI 3(實驗性版本) |
|---|---|
Windows.UI.Xaml.Controls.InkCanvas、 InkToolbar,以及其他 InkToolbar* 類型 |
Microsoft.UI.Xaml.Controls,相同類型名稱。 部分成員意見不同。 |
Windows.UI.Input.Inking.InkPresenter、InkStrokeContainer、InkStrokeInput、InkUnprocessedInput、InkSynchronizer、InkInputConfiguration、InkInputProcessingConfiguration、InkInputProcessingMode、InkInputRightDragAction、InkHighContrastAdjustment、InkStrokesCollectedEventArgs、InkStrokesErasedEventArgs |
Microsoft.UI.Xaml.Controls,相同的類型名稱。 部分成員意見不同。 |
Windows.UI.Input.Inking.InkStroke、InkDrawingAttributes、InkPoint、InkStrokeBuilder、InkPersistenceFormat、PenTipShape |
沒有變化 |
Windows.UI.Core.CoreInputDeviceTypes |
沒有變化 |
前兩列中的每個 UWP 類型都有一個同名的對應類型,但 Windows 應用程式 SDK 2.5.4-experimental 並未包含其所有成員:
-
InkStrokeContainer沒有公共建構子,且InkPresenter.StrokeContainer為唯讀。 要切換筆劃組,將每組筆劃儲存為SaveAsync,並用LoadAsync還原 ,以 取代當前筆劃。 -
SaveAsync並LoadAsync回傳IAsyncAction,因此不會回報進度,也SaveAsync不會回傳它寫入的位元組數。 -
InkStrokeContainer.GetRecognitionResults、InkStrokeContainer.UpdateRecognitionResults和InkInputConfiguration.IsPenHapticFeedbackEnabled無法使用。 -
InkToolbarRulerButton.Ruler和InkToolbar.IsRulerButtonCheckedChanged活動都無法使用。 UWP 已棄用InkToolbarRulerButton,改用InkToolbarStencilButton,所以請改用模板按鈕和InkToolbar.IsStencilButtonCheckedChanged事件。
Windows 應用程式 SDK 2.4.1 實驗性新增了橡皮擦 API,例如 InkToolbarEraserKind 和 InkToolbarEraserButton.SelectedEraser,而 2.5.4 實驗性移除了這些 API。 它們從未是 UWP 的一部分,所以不要把它們加到遷移的程式碼裡。 完整列表請參見 2.4.1 實驗變更。
因為像 InkPresenter 和 InkStrokeContainer 這類類型存在於兩個命名空間中,匯入 Microsoft.UI.Xaml.Controls 和 Windows.UI.Input.Inking 時使用這些名稱會產生錯誤 CS0104。 匯入 Microsoft.UI.Xaml.Controls,並為你使用的 Windows.UI.Input.Inking 類型新增別名指令:
using Microsoft.UI.Xaml.Controls;
using InkDrawingAttributes = Windows.UI.Input.Inking.InkDrawingAttributes;
using InkStroke = Windows.UI.Input.Inking.InkStroke;
InkStrokeContainer.SaveAsync 寫入 IOutputStream,並且 LoadAsync 從 IInputStream 讀取。 要使用 System.IO 檔案串流,請將其轉換為 AsOutputStream 和 AsInputStream。 檔案需要using System;來等候這些 Windows 執行階段 方法,以及 using System.IO; 和 using Windows.Storage.Streams;:
// Save ink strokes to a file.
using (FileStream fileStream = File.Create(filePath))
using (IOutputStream output = fileStream.AsOutputStream())
{
await inkStrokeContainer.SaveAsync(output);
await output.FlushAsync();
}
// Load ink strokes from a file.
using (FileStream fileStream = File.OpenRead(filePath))
using (IInputStream input = fileStream.AsInputStream())
{
await inkStrokeContainer.LoadAsync(input);
}
你可以繼續將工具列連接到畫布 InkToolbar.TargetInkCanvas,或者將 TargetInkPresenter 設定為直接連接到一個 InkPresenter。 要將自己的工具加入工具列,請使用 InkToolbarCustomToolButton 和 InkToolbarCustomToggleButton,如「 自訂墨水工具列」中所示。 遷移前,請檢視 已知的限制,例如尺規和量角器範本已回報的問題。
如果你的應用程式必須使用穩定版本,就沒有內建的手寫筆跡控制項可遷移至。 用 Win2D 渲染筆觸,自己處理指標輸入,或使用第三方墨跡程式庫。 要取代 AppBarToggleButton,請使用具有 CommandBar 個項目的 InkToolbar。
Win2D
Win2D 套件名稱更改,但 API 表面相同:
| UWP | WinUI 3 |
|---|---|
Win2D.uwp (NuGet) |
Microsoft.Graphics.Win2D (NuGet) |
所有 Microsoft.Graphics.Canvas.* API(CanvasDevice, CanvasBitmap, CanvasRenderTarget, DrawInk) 的運作方式都一樣。 只有 NuGet 套件參考需要更新。
列印
UWP 列印使用 PrintManager.GetForCurrentView()。 WinUI 3 需要視窗句柄互通:
// UWP
var printManager = PrintManager.GetForCurrentView();
printManager.PrintTaskRequested += OnPrintTaskRequested;
await PrintManager.ShowPrintUIAsync();
// WinUI 3 — must pass window handle
var hwnd = WinRT.Interop.WindowNative.GetWindowHandle(App.MainWindow);
var printManager = PrintManagerInterop.GetForWindow(hwnd);
printManager.PrintTaskRequested += OnPrintTaskRequested;
await PrintManagerInterop.ShowPrintUIForWindowAsync(hwnd);
PrintDocument渲染 API(Paginate, GetPreviewPage, AddPages) 保持不變。
Important
若省略視窗把手,則 PrintManagerInterop.GetForWindow 會拋出 COMException。 這與 FileOpenPicker 的互通模式相同——任何在 UWP 中使用 GetForCurrentView() 的 API,在 WinUI 3 中都需要視窗控制代碼。
啟動提示
I'm migrating a UWP app to WinUI 3 using the Windows App SDK.
Apply these substitutions:
- Windows.UI.Xaml.* → Microsoft.UI.Xaml.*
- CoreDispatcher / Dispatcher.RunAsync → DispatcherQueue.TryEnqueue
- ApplicationView → AppWindow + AppWindowTitleBar
- CoreWindow → Microsoft.UI.Xaml.Window
- MessageDialog → ContentDialog (set XamlRoot, not InitializeWithWindow)
- FileOpenPicker / FileSavePicker / FolderPicker → add InitializeWithWindow
- Windows.UI.Notifications → Microsoft.Windows.AppNotifications
- SystemNavigationManager.BackRequested → NavigationView back handling
- Pivot → TabView, NavigationView (top mode), or RadioButtons (no direct equivalent)
- InkCanvas, InkToolbar, InkPresenter, InkStrokeContainer, and the other ink input types → Microsoft.UI.Xaml.Controls (experimental Windows App SDK releases only; if the project uses a stable release, flag them instead)
- Ink members that Windows App SDK 2.5.4-experimental doesn't include (new InkStrokeContainer(), assignments to InkPresenter.StrokeContainer, GetRecognitionResults, UpdateRecognitionResults, IsPenHapticFeedbackEnabled, InkToolbarRulerButton.Ruler, IsRulerButtonCheckedChanged, and progress or byte counts from SaveAsync and LoadAsync) → flag them
- PrintManager.GetForCurrentView → PrintManagerInterop.GetForWindow(hwnd)
- Win2D.uwp NuGet → Microsoft.Graphics.Win2D NuGet
- InkStroke, InkDrawingAttributes, InkPoint, InkStrokeBuilder → no change (Windows.UI.Input.Inking; use using aliases to avoid CS0104)
Do not use any Windows.UI.Xaml.* namespaces in new code.
Do not use CoreDispatcher — use DispatcherQueue.
Do not add InkToolbarEraserKind, SelectedEraser, or other eraser APIs from Windows App SDK 2.4.1-experimental. Version 2.5.4-experimental removed them.
x:Bind defaults to Mode=OneTime. Add Mode=OneWay for any binding that should update at runtime.
Flag any APIs without a direct WinUI 3 equivalent rather than guessing.
專案檔案變更
取代UWP目標框架:
<!-- Before (UWP) -->
<TargetPlatformVersion>10.0.19041.0</TargetPlatformVersion>
<TargetPlatformMinVersion>10.0.17763.0</TargetPlatformMinVersion>
<!-- After (WinUI 3) -->
<TargetFramework>net10.0-windows10.0.19041.0</TargetFramework>
<WindowsSdkPackageVersion>10.0.19041.31</WindowsSdkPackageVersion>
新增 Windows 應用程式 SDK 套件:
dotnet add package Microsoft.WindowsAppSDK
相關內容
- Windows 應用程式 SDK 遷移指南 — 完整手動遷移導覽
- 將 UWP API 與函式庫映射至 Windows 應用程式 SDK — 完整的 API 映射表
- 從 UWP 遷移到 WinUI 時支援的是什麼 ——功能支援狀態
- 從 WPF 移轉