本教學課程的這個部分介紹數據檢視和模型的概念。
在教學課程的先前步驟中,您已將新頁面新增至專案,讓使用者儲存、編輯或刪除單一附注。 不過,因為應用程式需要處理一個以上的筆記,所以您需要新增另一個頁面來顯示所有附註(呼叫它 AllNotesPage)。 此頁面讓使用者選擇在編輯器頁面中開啟的附註,讓他們可以檢視、編輯或刪除它。 它也應該讓使用者建立新的附註。
若要達成此目的, AllNotesPage 必須有附注的集合,以及顯示集合的方法。 這是應用程式發生問題的地方,因為記事數據緊密系結至 NotePage 檔案。 在 中 AllNotesPage,您只想在清單或其他集合檢視中顯示所有筆記,其中包含每個附註的相關信息,例如建立日期和文字預覽。 由於記事文字與控件緊密系結 TextBox ,因此無法執行此動作。
在您新增頁面以顯示所有筆記之前,讓我們進行一些變更,以分隔筆記簡報中的記事數據。
檢視和模型
一般而言,WinUI 應用程式至少有 檢視層 和數據 層。
檢視層會使用 XAML 標記來定義 UI。 標記包含數據系結運算式(例如 x:Bind),可定義特定 UI 元件與數據成員之間的連線。 程序代碼後置檔案有時會當做檢視層的一部分使用,以包含自定義或作 UI 所需的額外程式碼,或在呼叫對數據執行工作的方法之前,從事件處理程式自變數擷取數據。
數據層或 模型會定義代表應用程式數據和相關邏輯的類型。 此圖層與檢視層無關,您可以建立多個與數據互動的不同檢視。
目前, NotePage 代表數據檢視(附註文字)。 不過,從系統檔案將數據讀入應用程式之後,它只存在於 Text 中的屬性 TextBox 中 NotePage。 它不會以可讓您以不同方式或不同位置呈現數據的方式在應用程式中表示;也就是說,應用程式沒有數據層。 您現在將重新建構專案,以建立數據層。
分隔檢視和模型
小提示
您可以從 GitHub 存放庫下載或檢視本教學課程的程式代碼。 若要查看此步驟中的程序代碼,請參閱此認可: 附注頁面 - 檢視模型。
重構現有的程序代碼,以將模型與檢視區隔開。 接下來的幾個步驟會組織程序代碼,讓檢視和模型彼此分開定義。
在 [方案總管] 中,以滑鼠右鍵按一下專案WinUINotes,然後選取 [新增>資料夾]。 將資料夾命名為 Models。
再次右鍵單擊項目 WinUINotes ,然後選擇 添加>新文件夾。 將資料夾命名為 Views。
找到項目 NotePage.xaml 並將其拖曳到資料夾中 Views 。 NotePage.xaml.cs檔案應該隨之移動。
備註
當您移動檔案時,Visual Studio 通常會提示您提示移動作業可能需要很長的時間。 如果您看到這個警告,就不應該在這裡發生問題,請按 [確定 ]。
Visual Studio 也可能詢問您是否要調整已移動檔案的命名空間。 選取 否。 您將在後續步驟中變更命名空間。
更新檢視命名空間
現在,檢視已移至 Views 資料夾,您必須更新命名空間以符合。 頁面 XAML 和程式碼後置檔案的命名空間會設定為 WinUINotes。 這必須更新為 WinUINotes.Views。
在 [方案總管 ] 窗格中,展開 NotePage.xaml 以顯示程式碼後置檔案。
按兩下項目 NotePage.xaml.cs 以開啟程式碼編輯器(如果尚未開啟)。 將命名空間變更為
WinUINotes.Views:namespace WinUINotes.Views按兩下項目 NotePage.xaml 以開啟 XAML 編輯器 (如果尚未開啟)。 舊命名空間會透過
x:Class屬性來參考,該屬性會定義哪個類別類型是 XAML 的程式代碼後置。 此專案不只是命名空間,而是具有 型別的命名空間。 將x:Class值變更為WinUINotes.Views.NotePage:x:Class="WinUINotes.Views.NotePage"
修正MainWindow中的命名空間參考
在上一個步驟中,您已建立記事頁面,並更新 MainWindow.xaml 為流覽至該頁面。 請記住,它已與 local: 命名空間對應對應。 常見的做法是將名稱 local 對應至專案的根命名空間,而Visual Studio專案範本已為您執行此動作(xmlns:local="using:WinUINotes")。 現在頁面已移至新的命名空間,XAML 中的類型對應現在無效。
幸運的是,您可以視需要新增自己的命名空間對應。 您必須這樣做,才能存取您在專案中建立的不同資料夾中的專案。 這個新的 XAML 命名空間會對應至 的 WinUINotes.Views命名空間,因此請將它命名為 views。 宣告看起來應該像下列屬性: xmlns:views="using:WinUINotes.Views"。
在 [ 方案總管] 窗格中,按兩下 MainWindow.xaml 專案,在 XAML 編輯器中開啟它。
在 對應下方的這一行中新增這個新的命名空間對應
local:xmlns:views="using:WinUINotes.Views"localXAML 命名空間是用來設定Frame.SourcePageType屬性,因此請將它變更為views該處。 您的 XAML 現在看起來應該像這樣:<Window x:Class="WinUINotes.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:WinUINotes" xmlns:views="using:WinUINotes.Views" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" Title="WinUI Notes"> <!-- ... Unchanged XAML not shown. --> <Frame x:Name="rootFrame" Grid.Row="1" SourcePageType="views:NotePage"/> <!-- ... Unchanged XAML not shown. --> </Window>建置並執行應用程式。 應用程式應該在沒有任何編譯程式錯誤的情況下執行,而且所有專案都應該如先前一樣運作。
定義模型
目前,模型(數據)內嵌在附注檢視中。 您將建立新的類別來代表記事頁面的資料:
在 [方案總管] 窗格中,以滑鼠右鍵按兩下Models資料夾,然後選取[新增>類別...]。
為類別Note.cs命名,然後按 [新增]。 該 Note.cs 檔案將在程式碼編輯器中開啟。
將檔案中的 Note.cs 程式碼替換為以下程式碼,該程式碼會製作類別
public並新增處理註釋的屬性和方法:using System; using System.Threading.Tasks; using Windows.Storage; namespace WinUINotes.Models { public class Note { private StorageFolder storageFolder = ApplicationData.Current.LocalFolder; public string Filename { get; set; } = string.Empty; public string Text { get; set; } = string.Empty; public DateTime Date { get; set; } = DateTime.Now; public Note() { Filename = "notes" + DateTime.Now.ToBinary().ToString() + ".txt"; } public async Task SaveAsync() { // Save the note to a file. StorageFile noteFile = (StorageFile)await storageFolder.TryGetItemAsync(Filename); if (noteFile is null) { noteFile = await storageFolder.CreateFileAsync(Filename, CreationCollisionOption.ReplaceExisting); } await FileIO.WriteTextAsync(noteFile, Text); } public async Task DeleteAsync() { // Delete the note from the file system. StorageFile noteFile = (StorageFile)await storageFolder.TryGetItemAsync(Filename); if (noteFile is not null) { await noteFile.DeleteAsync(); } } } }儲存檔案。
您會注意到,這段程式碼與 中的程式碼 NotePage.xaml.cs非常相似,但有一些更改和新增。
Filename 和 Text 已變更為 public 屬性,並 Date 已新增屬性。
儲存和刪除檔案的程式代碼已放在方法中 public 。 這大多與您在 中的Click按鈕NotePage事件處理程式中使用的程式代碼相同,但刪除檔案之後更新檢視的額外程序代碼。 這裡不需要它,因為您將使用數據系結來保持模型和檢視同步。
這些異步方法簽章會傳回 Task , void而不是 。 類別 Task 代表不會傳回值的單一異步作。 除非方法簽章需要 void,否則事件處理程序的情況 Click 是, async 方法應該會傳 Task回 。
您也不會再保留保留附註之 的參考 StorageFile 。 當您需要檔案來儲存或刪除檔案時,您只要嘗試取得檔案即可。
在 中 NotePage,您使用了檔名的佔位元: note.txt。 現在應用程式支援多個附註,儲存筆記的檔名必須不同且是唯一的。 若要這樣做,請在建構函式中設定 Filename 屬性。 您可以使用 DateTime.ToBinary 方法來根據目前時間建立檔名的一部分,並讓檔名是唯一的。 產生的檔名如下所示: notes-8584626598945870392.txt。
更新附註頁面
現在,您可以更新 NotePage 檢視以使用 Note 資料模型,並刪除移至 Note 模型的程序代碼。
開啟 Views\NotePage.xaml.cs 檔案 (如果尚未在編輯器中開啟)。
在頁面頂端的最後
using一個using語句之後,新增語句,讓您的程式代碼存取資料夾和命名空間中的Models類別。using WinUINotes.Models;從類別中移除這些行:
private StorageFolder storageFolder = ApplicationData.Current.LocalFolder; private StorageFile? noteFile = null; private string fileName = "note.txt";相反地,請在其位置新增
Note名為noteModel的物件。 這表示提供 檢視的附註數據NotePage。private Note? noteModel;NotePage_Loaded您也不再需要事件處理程式。 您不會直接從文字文件讀取文字到 TextBox。 相反地,記事文字會讀入Note物件中。 當您在稍後的步驟中新增 時,將會新增AllNotesPage此程序代碼。 刪除這幾行。Loaded += NotePage_Loaded; ... private async void NotePage_Loaded(object sender, RoutedEventArgs e) { noteFile = (StorageFile)await storageFolder.TryGetItemAsync(fileName); if (noteFile is not null) { NoteEditor.Text = await FileIO.ReadTextAsync(noteFile); } }以下欄方式取代 方法中的
SaveButton_Click程式碼:if (noteModel is not null) { await noteModel.SaveAsync(); }以下欄方式取代 方法中的
DeleteButton_Click程式碼:if (noteModel is not null) { await noteModel.DeleteAsync(); }
現在您可以更新 XAML 檔案以使用 Note 模型。 先前,您會直接從文本文件讀取文字到 TextBox.Text 程序代碼後置檔案中的 屬性。 現在,您會使用 Text 屬性的數據系結。
開啟 Views\NotePage.xaml 檔案 (如果尚未在編輯器中開啟)。
Text將屬性新增至TextBox控件。 將它系結至Text的noteModel屬性:Text="{x:Bind noteModel.Text, Mode=TwoWay}"。Header更新 以繫結至Date的noteModel屬性:Header="{x:Bind noteModel.Date.ToString()}"。<TextBox x:Name="NoteEditor" <!-- ↓ Add this line. ↓ --> Text="{x:Bind noteModel.Text, Mode=TwoWay}" AcceptsReturn="True" TextWrapping="Wrap" PlaceholderText="Enter your note" <!-- ↓ Update this line. ↓ --> Header="{x:Bind noteModel.Date.ToString()}" ScrollViewer.VerticalScrollBarVisibility="Auto" Width="400" Grid.Column="1"/>
數據系結是應用程式 UI 顯示資料的方式,並選擇性地與該資料保持同步。 系 Mode=TwoWay 結上的設定表示 TextBox.Text 和 noteModel.Text 屬性會自動同步處理。 在 中TextBox更新文字時,變更會反映在 的Text屬性中noteModel,如果noteModel.Text已變更,更新就會反映在 中TextBox。
屬性Header會使用的預設值ModeOneTime,因為 noteModel.Date 屬性在建立檔案之後不會變更。 此程式代碼也會示範稱為函式系x:Bind的強大功能,可讓您使用像是ToString系結路徑中的步驟之類的函式。
這很重要
請務必選擇正確的 BindingMode;否則,您的數據系結可能無法如預期般運作。 (常見的錯誤{x:Bind}是忘記在需要或BindingMode時OneWay變更預設值TwoWay。
| 名稱 | Description |
|---|---|
OneTime |
只有在建立系結時,才會更新目標屬性。 預設值為 {x:Bind}。 |
OneWay |
在建立系結時更新目標屬性。 來源物件的變更也可以傳播至目標。 預設值為 {Binding}。 |
TwoWay |
在任一變更時更新目標或來源物件。 建立系結時,會從來源更新目標屬性。 |
數據系結支持數據與UI的區隔,這會導致更簡單的概念模型,以及更好的可讀性、可測試性和應用程式可維護性。
在 WinUI 中,有兩種類型的系結可供您選擇:
- 標記
{x:Bind}延伸會在編譯階段處理。 其一些優點是改善系結表達式的效能和編譯時間驗證。 建議在 WinUI 應用程式中系結。 - 標記
{Binding}延伸會在運行時間進行處理,並使用一般用途運行時間物件檢查。
在檔中深入瞭解:
數據系結和MVVM
Model-View-ViewModel (MVVM) 是一種 UI 架構設計模式,用於分離與 .NET 開發人員常用的 UI 和非 UI 程式代碼。 當您深入瞭解建立 WinUI 應用程式時,您可能會看到並聽到提及。 如同您在這裡完成的一樣,分隔檢視和模型是完成應用程式完整MVVM實作的第一個步驟,但就您在本教學課程中而言。
備註
我們已使用「模型」一詞來參考本教學課程中的數據模型,但請務必注意,此模型在完整的MVVM實作中會更緊密地與 ViewModel 一致,同時納入 模型的各個層面。
若要深入瞭解MVVM,請參閱下列資源:
- Windows 數據系結和MVVM
- MVVM 工具組簡介
- YouTube 上 WinUI 和 WPF 開發的MVVM 建置組塊。