建立 .NET MAUI 應用程式
本教學課程系列旨在示範如何建立僅使用跨平台程序代碼的 .NET 多平臺應用程式 UI (.NET MAUI) 應用程式。 也就是說,您撰寫的程式代碼不會專屬於 Windows、Android、iOS 或 macOS。 您將建立的應用程式將會是記事應用程式,用戶可以在其中建立、儲存及載入多個筆記。
在本教學課程中,您會了解如何:
- 建立 .NET MAUI Shell 應用程式。
- 在您選擇的平台上執行您的應用程式。
- 使用 eXtensible Application Markup Language (XAML) 定義使用者介面,並透過程式代碼與 XAML 元素互動。
- 建立檢視並將其系結至數據。
- 使用導覽來往和移出頁面。
您將使用 Visual Studio 2022 建立應用程式,以便輸入附註並將其儲存至裝置記憶體。 最終的應用程式如下所示:
建立專案
開始本教學課程之前,您必須先遵循 建置您的第一個應用程式一文。 建立專案時,請使用下列設定:
專案名稱
這必須設定為
Notes
。 如果項目的名稱不同,您從本教學課程複製並貼上的程式代碼可能會導致建置錯誤。將解決方案和專案放置於同一個目錄
取消核取此設定。
建立專案時,請選擇最新的 .NET Framework。
選取目標裝置
.NET MAUI 應用程式已設計為可在多個作業系統和裝置上執行。 您將需要選取想要用來測試並偵錯應用程式的目標。
將 Visual Studio 工具列中的 [偵錯目標] 設定為要進行偵錯及測試的裝置。 下列步驟示範將 [ 偵錯目標 ] 設定為 Android:
- 選取 [ 偵錯目標] 下拉式按鈕。
- 選取 [Android 模擬器] 專案。
- 選取模擬器裝置。
自訂應用程式殼層
當 Visual Studio 建立 .NET MAUI 專案時,會產生四個重要的程式代碼檔案。 您可以在 Visual Studio 的 [方案總管] 窗格中看到:
這些檔案有助於設定和執行 .NET MAUI 應用程式。 每個檔案都有不同的用途,如下所述:
MauiProgram.cs
這是啟動應用程式的程式代碼檔案。 此檔案中的程式代碼可作為應用程式的跨平臺進入點,其會設定並啟動應用程式。 範本啟動程式代碼會指向
App
App.xaml檔案所定義的類別。App.xaml 和 App.xaml.cs
為了保持簡單,這兩個檔案都稱為單一檔案。 一般而言,有兩個檔案具有任何 XAML 檔案、.xaml 檔案本身,以及對應程式代碼檔案,其為 方案總管 中的子專案。 .xaml 檔案包含 XAML 標記,而程式代碼檔案則包含使用者建立的程式代碼,以與 XAML 標記互動。
App.xaml 檔案包含全應用程式 XAML 資源,例如色彩、樣式或範本。 App.xaml.cs檔案通常包含具現化 Shell 應用程式的程式代碼。 在此專案中,它會指向 類別
AppShell
。AppShell.xaml 和 AppShell.xaml.cs
此檔案會定義 類別
AppShell
,用來定義應用程式的視覺階層。MainPage.xaml 和 MainPage.xaml.cs
這是應用程式所顯示的啟動頁面。 MainPage.xaml 檔案會定義頁面的 UI(使用者介面)。 MainPage.xaml.cs包含 XAML 的程式代碼後置,例如按鈕點選事件的程式代碼。
新增 「關於」頁面
您將執行的第一個自訂是將另一個頁面新增至專案。 此頁面是「關於」頁面,代表此應用程式的相關信息,例如作者、版本,以及可能提供詳細信息的連結。
在 Visual Studio 的 [方案總管] 窗格中,以滑鼠右鍵按兩下 Notes 專案 >[新增>專案...]。
在 [ 新增專案 ] 對話框中,選取 視窗左側範本清單中的 .NET MAUI 。 接下來,選取 .NET MAUI ContentPage (XAML) 範本。 將檔案 命名為AboutPage.xaml,然後選取[ 新增]。
AboutPage.xaml 檔案會開啟新的文件索引標籤,顯示代表頁面 UI 的所有 XAML 標記。 將 XAML 標記取代為下列標記:
<?xml version="1.0" encoding="utf-8" ?> <ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="Notes.AboutPage"> <VerticalStackLayout Spacing="10" Margin="10"> <HorizontalStackLayout Spacing="10"> <Image Source="dotnet_bot.png" SemanticProperties.Description="The dot net bot waving hello!" HeightRequest="64" /> <Label FontSize="22" FontAttributes="Bold" Text="Notes" VerticalOptions="End" /> <Label FontSize="22" Text="v1.0" VerticalOptions="End" /> </HorizontalStackLayout> <Label Text="This app is written in XAML and C# with .NET MAUI." /> <Button Text="Learn more..." Clicked="LearnMore_Clicked" /> </VerticalStackLayout> </ContentPage>
按 CTRL+S 或選取 [檔案>儲存 AboutPage.xaml] 功能表來儲存盤案。
請細分頁面上所放置 XAML 控制項的主要部分:
<ContentPage>
是類別的AboutPage
根物件。<VerticalStackLayout>
是的唯一子物件 ContentPage。 ContentPage 只能有一個子物件。 此 VerticalStackLayout 類型可以有多個子系。 此版面配置控制項會逐一垂直排列其子系。<HorizontalStackLayout>
操作方式與<VerticalStackLayout>
相同,但其子系會水平排列。<Image>
顯示影像,在此情況下,它會使用dotnet_bot.png
每個 .NET MAUI 專案隨附的映像。重要
新增至項目的檔案實際上是
dotnet_bot.svg
。 .NET MAUI 會根據目標裝置,將可調整向量圖形 (SVG) 檔案轉換為可攜式網路圖形 (PNG) 檔案。 因此,將 SVG 檔案新增至 .NET MAUI 應用程式專案時,應該從具有擴展名的 XAML 或 C#.png
加以參考。 SVG 檔案的唯一參考應該在您的項目檔中。<Label>
會控制顯示文字。<Button>
控制項可由引發 事件的使用者Clicked
按下。 您可以執行程式碼來回應Clicked
事件。Clicked="LearnMore_Clicked"
Clicked
按鈕的事件會指派給LearnMore_Clicked
事件處理程式,該事件處理程式會在程式代碼後置檔案中定義。 您將在下一個步驟中建立此程式碼。
處理 Clicked 事件
下一個步驟是新增按鈕 Clicked
事件的程式碼。
在 Visual Studio 的 [方案總管] 窗格中,展開 AboutPage.xaml 檔案,以顯示其程式代碼後置檔案AboutPage.xaml.cs。 然後,按兩下 AboutPage.xaml.cs 檔案,在程式碼編輯器中開啟它。
新增下列
LearnMore_Clicked
事件處理程式程式代碼,以將系統瀏覽器開啟至特定 URL:private async void LearnMore_Clicked(object sender, EventArgs e) { // Navigate to the specified URL in the system browser. await Launcher.Default.OpenAsync("https://aka.ms/maui"); }
請注意,
async
關鍵詞已新增至方法宣告,以允許在開啟系統瀏覽器時使用await
關鍵詞。按 CTRL+S 或選取 [檔案>儲存] 功能表來儲存盤案AboutPage.xaml.cs。
現在 XAML 和 程式代碼後置 AboutPage
已完成,您必須在應用程式中顯示它。
新增映像資源
某些控制項可以使用影像,以增強使用者與應用程式互動的方式。 在本節中,您將下載兩個您將在應用程式中使用的映像,以及兩個與iOS搭配使用的替代映像。
下載下列影像:
下載映射之後,您可以使用 檔案總管 將它們移至專案的 Resources\Images 資料夾。 此資料夾中的任何檔案都會自動包含在專案中作為 MauiImage 資源。 您也可以使用 Visual Studio 將影像新增至您的專案。 如果您手動移動影像,請略過下列程式。
重要
請勿略過下載 iOS 特定映射,必須完成本教學課程。
使用 Visual Studio 移動影像
在 Visual Studio 的 [方案總管] 窗格中,展開 [資源] 資料夾,以顯示 [影像] 資料夾。
提示
您可以使用 檔案總管 將影像直接拖放到 [影像] 資料夾頂端的 [方案總管] 窗格中。 這會自動將檔案移至資料夾,並將其包含在專案中。 如果您選擇拖放檔案,請忽略此程序的其餘部分。
以滑鼠右鍵按一下 [影像],然後選取 [新增] > [現有項目...]。
導覽至包含已下載影像的資料夾。
將篩選從檔案類型篩選變更為 [影像檔案]。
按住 CTRL 並按下您下載的每個影像,然後按 [新增]
修改應用程式殼層
如本文開頭所述,類別 AppShell
會定義應用程式的視覺階層,也就是用來建立應用程式 UI 的 XAML 標記。 更新 XAML 以新增 TabBar 控制項:
按兩下 [方案總管] 窗格中的 AppShell.xaml 檔案,以開啟 XAML 編輯器。 將 XAML 標記取代為下列程式碼:
<?xml version="1.0" encoding="UTF-8" ?> <Shell x:Class="Notes.AppShell" xmlns="http://schemas.microsoft.com/dotnet/2021/maui" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:local="clr-namespace:Notes" Shell.FlyoutBehavior="Disabled"> <TabBar> <ShellContent Title="Notes" ContentTemplate="{DataTemplate local:MainPage}" Icon="{OnPlatform 'icon_notes.png', iOS='icon_notes_ios.png', MacCatalyst='icon_notes_ios.png'}" /> <ShellContent Title="About" ContentTemplate="{DataTemplate local:AboutPage}" Icon="{OnPlatform 'icon_about.png', iOS='icon_about_ios.png', MacCatalyst='icon_about_ios.png'}" /> </TabBar> </Shell>
按 CTRL+S 或選取 [檔案>儲存 AppShell.xaml] 選單來儲存盤案。
讓我們細分 XAML 的主要部分:
<Shell>
是 XAML 標記的根物件。<TabBar>
是的內容 Shell。- 內的
<TabBar>
兩個<ShellContent>
物件。 在您取代範本程式代碼之前,有一個指向頁面的單<ShellContent>
一物件MainPage
。
TabBar
及其子系不會代表任何使用者介面元素,而是代表應用程式視覺階層的組織。 Shell 會採用這些物件,併產生內容的使用者介面,頂端有代表每個頁面的列。 ShellContent.Icon
每個頁面的 屬性都會使用特殊語法:{OnPlatform ...}
。 當 XAML 頁面針對每個平台進行編譯時,會處理此語法,而且您可以為每個平臺指定屬性值。 在這裡情況下,每個平台預設都會使用 icon_about.png
圖示,但 iOS 和 MacCatalyst 會使用 icon_about_ios.png
。
每個 <ShellContent>
物件都指向要顯示的頁面。 這是由 ContentTemplate
屬性所設定。
執行應用程式
按 F5 或按下 Visual Studio 頂端的播放按鈕來執行應用程式:
您會看到有兩個索引標籤: 附註 和 關於。 按 [ 關於] 索引標籤,應用程式會巡覽至您所建立的 AboutPage
。 按 [ 深入瞭解...] 按鈕以開啟網頁瀏覽器。
關閉應用程式並返回 Visual Studio。 如果您使用 Android 模擬器,請在虛擬裝置中終止應用程式,或按 Visual Studio 頂端的停止按鈕:
建立附注的頁面
現在,應用程式包含 MainPage
和 AboutPage
,您可以開始建立應用程式的其餘部分。 首先,您將建立一個頁面,讓使用者建立和顯示附註,然後撰寫程式代碼來載入並儲存筆記。
記事頁面會顯示附註,並允許您儲存或刪除它。 首先,將新頁面新增至專案:
在 Visual Studio 的 [方案總管] 窗格中,以滑鼠右鍵按兩下 Notes 專案 >[新增>專案...]。
在 [ 新增專案 ] 對話框中,選取 視窗左側範本清單中的 .NET MAUI 。 接下來,選取 .NET MAUI ContentPage (XAML) 範本。 將檔案 命名為 NotePage.xaml,然後選取 [ 新增]。
NotePage.xaml 檔案將會在新索引標籤中開啟,並顯示代表頁面 UI 的所有 XAML 標記。 取代 XAML 程式代碼標記下列標記:
<?xml version="1.0" encoding="utf-8" ?> <ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="Notes.NotePage" Title="Note"> <VerticalStackLayout Spacing="10" Margin="5"> <Editor x:Name="TextEditor" Placeholder="Enter your note" HeightRequest="100" /> <Grid ColumnDefinitions="*,*" ColumnSpacing="4"> <Button Text="Save" Clicked="SaveButton_Clicked" /> <Button Grid.Column="1" Text="Delete" Clicked="DeleteButton_Clicked" /> </Grid> </VerticalStackLayout> </ContentPage>
按 CTRL + S 或選取 [檔案>儲存 NotePage.xaml] 選單來儲存盤案。
請細分頁面上所放置 XAML 控制項的主要部分:
<VerticalStackLayout>
垂直排列其子控件,另一個則下方。<Editor>
是多行文本編輯器控件,而且 是 內的 VerticalStackLayout第一個控件。<Grid>
是版面配置控件,而 是 內的 VerticalStackLayout第二個控件。此控件會定義要建立儲存格的數據行和數據列。 子控件會放在這些儲存格內。
根據預設, Grid 控件會包含單一數據列和數據行,並建立單一單元格。 數據行是以寬度定義,而
*
寬度的值會告知數據行盡可能填滿空間。 前一個代碼段定義了兩個數據行,兩個數據行都盡可能多地使用空間,這會平均分散配置空間中的數據行:ColumnDefinitions="*,*"
。 數據行大小會以,
字元分隔。由定義的數據行和數據列會 Grid 從 0 開始編製索引。 因此,第一個數據行會是索引 0,第二個數據行是索引 1,依故。
兩
<Button>
個控件位於 內<Grid>
並指派數據行。 如果子控件未定義數據行指派,它會自動指派給第一個數據行。 在此標記中,第一個按鈕是 [儲存] 按鈕,並自動指派給第一個數據行數據行 0。 第二個按鈕是 [刪除] 按鈕,並指派給第二欄第 1 欄。請注意,這兩個按鈕已
Clicked
處理 事件。 您將在下一節中新增這些處理程式的程式代碼。
載入並儲存附註
開啟程式代碼後置檔案NotePage.xaml.cs。 您可以透過三種方式開啟 NotePage.xaml 檔案的程式代碼後置:
- 如果 NotePage.xaml 已開啟,而且是正在編輯的作用中檔,請按 F7。
- 如果 NotePage.xaml 已開啟且正在編輯使用中檔,請在文字編輯器中按兩下滑鼠右鍵,然後選取 [檢視程式代碼]。
- 使用 方案總管 展開 NotePage.xaml 專案,以顯示NotePage.xaml.cs檔案。 按兩下檔案以開啟它。
當您新增 XAML 檔案時,程式代碼後置會在建構函式中包含單一行,這是對 方法的 InitializeComponent
呼叫:
namespace Notes;
public partial class NotePage : ContentPage
{
public NotePage()
{
InitializeComponent();
}
}
方法 InitializeComponent
會讀取 XAML 標記,並初始化標記所定義的所有物件。 物件會以其父子式關聯性連接,而且程式代碼中定義的事件處理程式會附加至 XAML 中設定的事件。
既然您已進一步瞭解程式代碼後置檔案,您要將程式代碼新增至 程式代碼後置檔案NotePage.xaml.cs 程式代碼後置檔案,以處理載入和儲存筆記。
建立附注時,它會以文本檔的形式儲存到裝置。 檔案的名稱是由
_fileName
變數表示。 將下列string
變數宣告新增至NotePage
類別:public partial class NotePage : ContentPage { string _fileName = Path.Combine(FileSystem.AppDataDirectory, "notes.txt");
上述程式代碼會建構檔案的路徑,並將它儲存在應用程式的本機數據目錄中。 檔名notes.txt。
在 類別的建構函式中,呼叫 方法之後
InitializeComponent
,從裝置讀取檔案,並將其內容儲存在TextEditor
控件的Text
屬性中:public NotePage() { InitializeComponent(); if (File.Exists(_fileName)) TextEditor.Text = File.ReadAllText(_fileName); }
接下來,新增程式代碼以處理
Clicked
XAML 中定義的事件:private void SaveButton_Clicked(object sender, EventArgs e) { // Save the file. File.WriteAllText(_fileName, TextEditor.Text); } private void DeleteButton_Clicked(object sender, EventArgs e) { // Delete the file. if (File.Exists(_fileName)) File.Delete(_fileName); TextEditor.Text = string.Empty; }
方法
SaveButton_Clicked
會將控件中的 Editor 文字寫入變數所代表的_fileName
檔案。方法
DeleteButton_Clicked
會先檢查變數所代表的_fileName
檔案,如果檔案存在,則會將其刪除。 接下來, Editor 會清除控件的文字。按 CTRL + S 或選取 [檔案>儲存] 功能表,以儲存盤案NotePage.xaml.cs。
程序代碼後置檔案的最終程式代碼看起來應該如下所示:
namespace Notes;
public partial class NotePage : ContentPage
{
string _fileName = Path.Combine(FileSystem.AppDataDirectory, "notes.txt");
public NotePage()
{
InitializeComponent();
if (File.Exists(_fileName))
TextEditor.Text = File.ReadAllText(_fileName);
}
private void SaveButton_Clicked(object sender, EventArgs e)
{
// Save the file.
File.WriteAllText(_fileName, TextEditor.Text);
}
private void DeleteButton_Clicked(object sender, EventArgs e)
{
// Delete the file.
if (File.Exists(_fileName))
File.Delete(_fileName);
TextEditor.Text = string.Empty;
}
}
測試附註
現在記事頁面已完成,您需要一種方式來向用戶呈現它。 開啟 AppShell.xaml 檔案,並將第一個項目ShellContent變更為指向 ,NotePage
而不是 MainPage
:
<?xml version="1.0" encoding="UTF-8" ?>
<Shell
x:Class="Notes.AppShell"
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:Notes"
Shell.FlyoutBehavior="Disabled">
<TabBar>
<ShellContent
Title="Notes"
ContentTemplate="{DataTemplate local:NotePage}"
Icon="{OnPlatform 'icon_notes.png', iOS='icon_notes_ios.png', MacCatalyst='icon_notes_ios.png'}" />
<ShellContent
Title="About"
ContentTemplate="{DataTemplate local:AboutPage}"
Icon="{OnPlatform 'icon_about.png', iOS='icon_about_ios.png', MacCatalyst='icon_about_ios.png'}" />
</TabBar>
</Shell>
儲存盤案並執行應用程式。 請嘗試在輸入方塊中輸入,然後按 [ 儲存] 按鈕。 關閉應用程式,然後重新開啟它。 您輸入的附註應該從裝置的記憶體載入。
將數據系結至UI並瀏覽頁面
本教學課程的這個部分介紹檢視、模型和應用程式內導覽的概念。
在教學課程的先前步驟中,您已將兩個頁面新增至專案: NotePage
和 AboutPage
。 頁面代表數據的檢視。 NotePage
是顯示「記事數據」的「檢視」,而 AboutPage
是顯示「應用程式資訊數據」的「檢視」。這兩個檢視都有該數據硬式編碼或內嵌的模型,您必須將數據模型與檢視區隔開。
將模型與檢視區隔的優點為何? 它可讓您設計檢視來表示和與模型的任何部分互動,而不必擔心實作模型的實際程序代碼。 這是使用數據系結完成的,本教學課程稍後將會呈現。 不過,現在,讓我們重新建構專案。
分隔檢視和模型
重構現有的程序代碼,以將模型與檢視區隔開。 接下來的幾個步驟會組織程序代碼,讓檢視和模型彼此分開定義。
從專案中,刪除 MainPage.xaml 和 MainPage.xaml.cs,因為已不再需要。 在 [方案總管] 窗格中,尋找 MainPage.xaml 的項目,並在其上按一下滑鼠右鍵,然後選取 [刪除]。
提示
刪除 MainPage.xaml 項目也應該刪除MainPage.xaml.cs專案。 如果未 刪除MainPage.xaml.cs ,請以滑鼠右鍵按兩下它,然後選取 [ 刪除]。
以滑鼠右鍵按兩下Notes項目,然後選取[新增>資料夾]。 將資料夾命名為 Models註冊免費試用帳戶。
以滑鼠右鍵按兩下Notes項目,然後選取[新增>資料夾]。 將資料夾命名為 Views註冊免費試用帳戶。
尋找 NotePage.xaml 專案,並將其拖曳至Views資料夾。 NotePage.xaml.cs應該隨它移動。
重要
當您移動檔案時,Visual Studio 通常會提示您提示移動作業可能需要很長的時間。 如果您看到這個警告,就不應該在這裡發生問題,請按 [確定 ]。
Visual Studio 也可能詢問您是否要調整已移動檔案的命名空間。 選取 [否 ],因為後續步驟會變更命名空間。
尋找 AboutPage.xaml 專案,並將其拖曳至Views資料夾。 AboutPage.xaml.cs應該隨它移動。
更新檢視命名空間
現在,檢視已移至 Views 資料夾,您必須更新命名空間以符合。 頁面 XAML 和程式碼後置檔案的命名空間會設定為 Notes
。 這必須更新為 Notes.Views
。
在 [方案總管] 窗格中,展開 NotePage.xaml 和 AboutPage.xaml,以顯示程式代碼後置檔案:
按兩下 NotePage.xaml.cs 項目以開啟程式代碼編輯器。 將命名空間變更為
Notes.Views
:namespace Notes.Views;
針對AboutPage.xaml.cs項目重複上述步驟。
按兩下 NotePage.xaml 項目以開啟 XAML 編輯器。 舊命名空間會透過
x:Class
屬性來參考,該屬性會定義哪個類別類型是 XAML 的程式代碼後置。 此專案不只是命名空間,而是具有 型別的命名空間。 將x:Class
值變更為Notes.Views.NotePage
:x:Class="Notes.Views.NotePage"
針對 AboutPage.xaml 項目重複上一個步驟,但將
x:Class
值設定為Notes.Views.AboutPage
。
修正 Shell 中的命名空間參考
AppShell.xaml 定義兩個索引標籤,一個用於 NotesPage
,另一個用於 AboutPage
。 現在,這兩個頁面已移至新的命名空間,XAML 中的類型對應現在無效。 在 [方案總管] 窗格中,按兩下AppShell.xaml專案,在 XAML 編輯器中開啟它。 它看起來應該像下列代碼段:
<?xml version="1.0" encoding="UTF-8" ?>
<Shell
x:Class="Notes.AppShell"
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:Notes"
Shell.FlyoutBehavior="Disabled">
<TabBar>
<ShellContent
Title="Notes"
ContentTemplate="{DataTemplate local:NotePage}"
Icon="{OnPlatform 'icon_notes.png', iOS='icon_notes_ios.png', MacCatalyst='icon_notes_ios.png'}" />
<ShellContent
Title="About"
ContentTemplate="{DataTemplate local:AboutPage}"
Icon="{OnPlatform 'icon_about.png', iOS='icon_about_ios.png', MacCatalyst='icon_about_ios.png'}" />
</TabBar>
</Shell>
.NET 命名空間會透過 XML 命名空間宣告匯入 XAML。 在先前的 XAML 標記中,它是 xmlns:local="clr-namespace:Notes"
根元素中的 屬性: <Shell>
。 宣告 XML 命名空間以在相同元件中匯入 .NET 命名空間的格式如下:
xmlns:{XML namespace name}="clr-namespace:{.NET namespace}"
因此,先前的宣告會將的 local
XML 命名空間對應至 的 Notes
.NET 命名空間。 常見的做法是將名稱 local
對應至專案的根命名空間。
local
拿掉 XML 命名空間並新增 XML 命名空間。 這個新的 XML 命名空間會對應至 的 Notes.Views
.NET 命名空間,因此請將它命名為 views
。 宣告看起來應該像下列屬性: xmlns:views="clr-namespace:Notes.Views"
。
屬性 local
使用 ShellContent.ContentTemplate
XML 命名空間,將它們變更為 views
。 您的 XAML 現在看起來應該像下列代碼段:
<?xml version="1.0" encoding="UTF-8" ?>
<Shell
x:Class="Notes.AppShell"
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:views="clr-namespace:Notes.Views"
Shell.FlyoutBehavior="Disabled">
<TabBar>
<ShellContent
Title="Notes"
ContentTemplate="{DataTemplate views:NotePage}"
Icon="{OnPlatform 'icon_notes.png', iOS='icon_notes_ios.png', MacCatalyst='icon_notes_ios.png'}" />
<ShellContent
Title="About"
ContentTemplate="{DataTemplate views:AboutPage}"
Icon="{OnPlatform 'icon_about.png', iOS='icon_about_ios.png', MacCatalyst='icon_about_ios.png'}" />
</TabBar>
</Shell>
您現在應該能夠執行應用程式,而不會發生任何編譯程序錯誤,而且所有專案仍應如先前般運作。
定義模型
模型目前是內嵌在附注和檢視的相關數據。 我們將建立新的類別來表示該數據。 首先,用來代表記事頁數據的模型:
在 [方案總管] 窗格中,以滑鼠右鍵按兩下Models資料夾,然後選取[新增>類別...]。
將類別 命名為Note.cs ,然後按 [新增]。
開啟 Note.cs ,並將程式代碼取代為下列代碼段:
namespace Notes.Models; internal class Note { public string Filename { get; set; } public string Text { get; set; } public DateTime Date { get; set; } }
儲存檔案。
接下來,建立關於頁面的模型:
在 [方案總管] 窗格中,以滑鼠右鍵按兩下Models資料夾,然後選取[新增>類別...]。
將類別 命名為About.cs ,然後按 [新增]。
開啟 About.cs ,並將程式代碼取代為下列代碼段:
namespace Notes.Models; internal class About { public string Title => AppInfo.Name; public string Version => AppInfo.VersionString; public string MoreInfoUrl => "https://aka.ms/maui"; public string Message => "This app is written in XAML and C# with .NET MAUI."; }
儲存檔案。
更新關於頁面
關於頁面將是更新最快的頁面,您將能夠執行應用程式,並查看其如何從模型載入數據。
在 [方案總管] 窗格中,開啟 Views\AboutPage.xaml 檔案。
將內容取代為下列代碼段:
<?xml version="1.0" encoding="utf-8" ?> <ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:models="clr-namespace:Notes.Models" x:Class="Notes.Views.AboutPage"> <ContentPage.BindingContext> <models:About /> </ContentPage.BindingContext> <VerticalStackLayout Spacing="10" Margin="10"> <HorizontalStackLayout Spacing="10"> <Image Source="dotnet_bot.png" SemanticProperties.Description="The dot net bot waving hello!" HeightRequest="64" /> <Label FontSize="22" FontAttributes="Bold" Text="{Binding Title}" VerticalOptions="End" /> <Label FontSize="22" Text="{Binding Version}" VerticalOptions="End" /> </HorizontalStackLayout> <Label Text="{Binding Message}" /> <Button Text="Learn more..." Clicked="LearnMore_Clicked" /> </VerticalStackLayout> </ContentPage>
讓我們看看已變更的行,其反白顯示於上一個代碼段:
xmlns:models="clr-namespace:Notes.Models"
這一行會將
Notes.Models
.NET 命名空間對應至models
XML 命名空間。的
BindingContext
ContentPage 屬性會使用 的 XML 命名空間和 物件,設定為 類別的Note.Models.About
models:About
實例。 這是使用 屬性項目語法 而不是 XML 屬性來設定。重要
到目前為止,屬性已使用 XML 屬性來設定。 這適用於簡單的值,例如
Label.FontSize
屬性。 但是,如果屬性值比較複雜,您必須使用 屬性元素語法 來建立物件。 請考慮使用其FontSize
屬性集建立標籤的下列範例:<Label FontSize="22" />
您可以使用屬性元素語法來設定相同的
FontSize
屬性:<Label> <Label.FontSize> 22 </Label.FontSize> </Label>
三
<Label>
個控件的Text
屬性值已從硬式編碼字串變更為系結語法:{Binding PATH}
。{Binding}
語法會在運行時間處理,允許從系結傳回的值是動態的。 的PATH
{Binding PATH}
一部分是要系結至的屬性路徑。 屬性來自目前控制件的BindingContext
。<Label>
使用 控件時,BindingContext
為 unset。 當控件未設定內容時,內容會繼承自父系,在此情況下,父物件設定內容是根物件: ContentPage。中的
BindingContext
物件是模型的實例About
。 其中一個標籤的系結路徑會將Label.Text
屬性系結至About.Title
屬性。
關於頁面的最終變更是更新開啟網頁的按鈕按兩下。 URL 在程式代碼後置中已硬式編碼,但URL應該來自屬性中的 BindingContext
模型。
在 [方案總管] 窗格中,開啟 Views\AboutPage.xaml.cs 檔案。
以下列程式碼取代
LearnMore_Clicked
方法:private async void LearnMore_Clicked(object sender, EventArgs e) { if (BindingContext is Models.About about) { // Navigate to the specified URL in the system browser. await Launcher.Default.OpenAsync(about.MoreInfoUrl); } }
如果您查看反白顯示的這一行,程式代碼會檢查 是否 BindingContext
為 Models.About
型別,如果為 ,則會將它指派給 about
變數。 語句內的 if
下一行會將瀏覽器開啟至 屬性所提供的 about.MoreInfoUrl
URL。
執行應用程式,您應該會看到它執行的方式與之前完全相同。 請嘗試變更模型值的相關信息,並查看瀏覽器開啟的UI和URL如何變更。
更新附註頁面
上一節會將 about 頁面檢視系結至 about 模型,現在您將執行相同的動作,將 note 檢視系結至 note 模型。 不過,在此情況下,不會在 XAML 中建立模型,但會在後續幾個步驟的程式代碼後置中提供。
在 [方案總管] 窗格中,開啟 Views\NotePage.xaml 檔案。
變更新增
<Editor>
屬性的Text
控件。 將 屬性系結至Text
屬性: :<Editor ... Text="{Binding Text}"
<?xml version="1.0" encoding="utf-8" ?> <ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="Notes.Views.NotePage" Title="Note"> <VerticalStackLayout Spacing="10" Margin="5"> <Editor x:Name="TextEditor" Placeholder="Enter your note" Text="{Binding Text}" HeightRequest="100" /> <Grid ColumnDefinitions="*,*" ColumnSpacing="4"> <Button Text="Save" Clicked="SaveButton_Clicked" /> <Button Grid.Column="1" Text="Delete" Clicked="DeleteButton_Clicked" /> </Grid> </VerticalStackLayout> </ContentPage>
程序代碼後置的修改比 XAML 複雜。 目前的程式代碼會在建構函式中載入檔案內容,然後將它直接設定為 TextEditor.Text
屬性。 以下是目前程式代碼的外觀:
public NotePage()
{
InitializeComponent();
if (File.Exists(_fileName))
TextEditor.Text = File.ReadAllText(_fileName);
}
建立新的 LoadNote
方法,而不是在建構函式中載入附注。 此方法會執行下列動作:
- 接受檔名參數。
- 建立新的記事模型並設定檔名。
- 如果檔案存在,請將它的內容載入模型。
- 如果檔案存在,請使用建立檔案的日期更新模型。
BindingContext
將頁面的 設定為模型。
在 [方案總管] 窗格中,開啟 Views\NotePage.xaml.cs 檔案。
將下列方法新增至 班級:
private void LoadNote(string fileName) { Models.Note noteModel = new Models.Note(); noteModel.Filename = fileName; if (File.Exists(fileName)) { noteModel.Date = File.GetCreationTime(fileName); noteModel.Text = File.ReadAllText(fileName); } BindingContext = noteModel; }
更新類別建構函式以呼叫
LoadNote
。 附註的檔名應該是隨機產生的名稱,以在應用程式的本機數據目錄中建立。public NotePage() { InitializeComponent(); string appDataPath = FileSystem.AppDataDirectory; string randomFileName = $"{Path.GetRandomFileName()}.notes.txt"; LoadNote(Path.Combine(appDataPath, randomFileName)); }
新增列出所有附注的檢視和模型
本教學課程的這個部分會新增應用程式的最後一個部分,此檢視會顯示先前建立的所有筆記。
多個附註和流覽
目前記 事檢視會顯示單一附註 。 若要顯示多個筆記,請建立新的檢視和模型: AllNotes。
- 在 [方案總管] 窗格中,以滑鼠右鍵按單擊Views資料夾,然後選取 [新增>專案...
- 在 [ 新增專案 ] 對話框中,選取 視窗左側範本清單中的 .NET MAUI 。 接下來,選取 .NET MAUI ContentPage (XAML) 範本。 將檔案 命名為 AllNotesPage.xaml,然後選取 [ 新增]。
- 在 [方案總管] 窗格中,以滑鼠右鍵按下Models資料夾,然後選取 [新增>類別...
- 將類別 命名為AllNotes.cs ,然後按 [新增]。
撰寫 AllNotes 模型的程式代碼
新的模型將代表顯示多個筆記所需的數據。 此數據會是代表附註集合的屬性。 集合將會是 ObservableCollection
特製化集合的 。 當列出多個專案的控件,例如 ListView, 系結至 ObservableCollection
時,兩者會一起運作,以自動讓專案清單與集合保持同步。 如果清單新增專案,則會更新集合。 如果集合加入專案,控件就會自動更新為新的專案。
在 [方案總管] 窗格中,開啟 Models\AllNotes.cs 檔案。
將程式代碼取代為下列代碼段:
using System.Collections.ObjectModel; namespace Notes.Models; internal class AllNotes { public ObservableCollection<Note> Notes { get; set; } = new ObservableCollection<Note>(); public AllNotes() => LoadNotes(); public void LoadNotes() { Notes.Clear(); // Get the folder where the notes are stored. string appDataPath = FileSystem.AppDataDirectory; // Use Linq extensions to load the *.notes.txt files. IEnumerable<Note> notes = Directory // Select the file names from the directory .EnumerateFiles(appDataPath, "*.notes.txt") // Each file name is used to create a new Note .Select(filename => new Note() { Filename = filename, Text = File.ReadAllText(filename), Date = File.GetLastWriteTime(filename) }) // With the final collection of notes, order them by date .OrderBy(note => note.Date); // Add each note into the ObservableCollection foreach (Note note in notes) Notes.Add(note); } }
上述程式代碼會宣告名為 Notes
的集合,並使用 LoadNotes
方法從裝置載入筆記。 此方法會使用 LINQ 擴充功能,將數據載入、轉換和排序至 Notes
集合。
設計 AllNotes 頁面
接下來,檢視必須設計為支援 AllNotes 模型。
在 [方案總管] 窗格中,開啟 Views\AllNotesPage.xaml 檔案。
以下列標記取代程式代碼:
<?xml version="1.0" encoding="utf-8" ?> <ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="Notes.Views.AllNotesPage" Title="Your Notes"> <!-- Add an item to the toolbar --> <ContentPage.ToolbarItems> <ToolbarItem Text="Add" Clicked="Add_Clicked" IconImageSource="{FontImage Glyph='+', Color=Black, Size=22}" /> </ContentPage.ToolbarItems> <!-- Display notes in a list --> <CollectionView x:Name="notesCollection" ItemsSource="{Binding Notes}" Margin="20" SelectionMode="Single" SelectionChanged="notesCollection_SelectionChanged"> <!-- Designate how the collection of items are laid out --> <CollectionView.ItemsLayout> <LinearItemsLayout Orientation="Vertical" ItemSpacing="10" /> </CollectionView.ItemsLayout> <!-- Define the appearance of each item in the list --> <CollectionView.ItemTemplate> <DataTemplate> <StackLayout> <Label Text="{Binding Text}" FontSize="22"/> <Label Text="{Binding Date}" FontSize="14" TextColor="Silver"/> </StackLayout> </DataTemplate> </CollectionView.ItemTemplate> </CollectionView> </ContentPage>
先前的 XAML 引進了一些新概念:
屬性
ContentPage.ToolbarItems
包含ToolbarItem
。 這個處定義的按鈕通常會顯示在應用程式頂端,並沿著頁面標題顯示。 不過,視平臺而定,它可能處於不同的位置。 按下其中一個按鈕時,Clicked
就會引發 事件,就像一般按鈕一樣。屬性會
ToolbarItem.IconImageSource
設定要顯示在按鈕上的圖示。 圖示可以是專案所定義的任何影像資源,但在此範例中會FontImage
使用 。FontImage
可以使用字型的單一圖像做為影像。控件 CollectionView 會顯示專案的集合,在此情況下,會系結至模型的
Notes
屬性。 集合檢視會透過CollectionView.ItemsLayout
和CollectionView.ItemTemplate
屬性設定每個專案的方式。針對集合中的每個專案,會產生
CollectionView.ItemTemplate
宣告的 XAML。BindingContext
該 XAML 的 會變成集合專案本身,在此案例中,每個個別的附註。 附註的範本會使用兩個標籤,這些標籤系結至附註的Text
和Date
屬性。會 CollectionView 處理
SelectionChanged
事件,此事件會在選取集合檢視中的項目時引發。
檢視的程式代碼後置必須寫入以載入附注並處理事件。
在 [方案總管] 窗格中,開啟 Views/AllNotesPage.xaml.cs 檔案。
將程式代碼取代為下列代碼段:
namespace Notes.Views; public partial class AllNotesPage : ContentPage { public AllNotesPage() { InitializeComponent(); BindingContext = new Models.AllNotes(); } protected override void OnAppearing() { ((Models.AllNotes)BindingContext).LoadNotes(); } private async void Add_Clicked(object sender, EventArgs e) { await Shell.Current.GoToAsync(nameof(NotePage)); } private async void notesCollection_SelectionChanged(object sender, SelectionChangedEventArgs e) { if (e.CurrentSelection.Count != 0) { // Get the note model var note = (Models.Note)e.CurrentSelection[0]; // Should navigate to "NotePage?ItemId=path\on\device\XYZ.notes.txt" await Shell.Current.GoToAsync($"{nameof(NotePage)}?{nameof(NotePage.ItemId)}={note.Filename}"); // Unselect the UI notesCollection.SelectedItem = null; } } }
此程式代碼會使用 建構函式將頁面的 設定 BindingContext
為模型。
方法 OnAppearing
會從基類覆寫。 每當顯示頁面時,就會自動呼叫這個方法,例如巡覽至頁面時。 這裡的程式代碼會告訴模型載入附註。 CollectionView由於 AllNotes 檢視中的 系結至 AllNotes 模型的 Notes
屬性,也就是 ObservableCollection
,每當載入筆記時,CollectionView就會自動更新 。
處理程序 Add_Clicked
引進了另一個新概念導覽。 由於應用程式使用 .NET MAUI Shell,因此您可以藉由呼叫 Shell.Current.GoToAsync
方法來巡覽至頁面。 請注意,處理程式是以 關鍵詞宣告 async
,這允許在巡覽時使用 await
關鍵詞。 此處理程式會巡覽至 NotePage
。
上一個代碼段中的最後一段程式代碼是 notesCollection_SelectionChanged
處理程式。 這個方法會採用目前選取的專案模型 Note ,並使用其資訊來巡覽至 NotePage
。 GoToAsync 會使用 URI 字串進行流覽。 在此情況下,會建構使用查詢字串參數在目的地頁面上設定屬性的字串。 表示 URI 的插補字串最終看起來類似下列字串:
NotePage?ItemId=path\on\device\XYZ.notes.txt
參數 ItemId=
會設定為儲存附註之裝置上的檔名。
Visual Studio 可能表示 NotePage.ItemId
屬性不存在,但屬性不存在。 下一個步驟是修改 Note 檢視 ,以根據您將建立的參數 ItemId
載入模型。
查詢字串參數
檢視Note必須支援查詢字串參數 ItemId
。 立即建立:
在 [方案總管] 窗格中,開啟 Views/NotePage.xaml.cs 檔案。
將
QueryProperty
屬性新增至class
關鍵詞,並提供查詢字串屬性的名稱,以及它對應至的類別屬性,ItemId
以及ItemId
分別對應至 的類別屬性:[QueryProperty(nameof(ItemId), nameof(ItemId))] public partial class NotePage : ContentPage
新增名為
ItemId
的新string
屬性。 這個屬性會呼叫LoadNote
方法,並傳遞 屬性的值,這反過來應該是附註的檔名:public string ItemId { set { LoadNote(value); } }
SaveButton_Clicked
以下欄程序代碼取代和DeleteButton_Clicked
處理程式:private async void SaveButton_Clicked(object sender, EventArgs e) { if (BindingContext is Models.Note note) File.WriteAllText(note.Filename, TextEditor.Text); await Shell.Current.GoToAsync(".."); } private async void DeleteButton_Clicked(object sender, EventArgs e) { if (BindingContext is Models.Note note) { // Delete the file. if (File.Exists(note.Filename)) File.Delete(note.Filename); } await Shell.Current.GoToAsync(".."); }
按鍵現在是
async
。 按下之後,頁面會使用的..
URI 來巡覽回上一頁。從程式
_fileName
代碼頂端刪除變數,因為它不再由類別使用。
修改應用程式的視覺化樹狀結構
AppShell
仍在載入單一記事頁面,而是需要載入AllPages檢視。 開啟 AppShell.xaml 檔案,並將第一個項目ShellContent變更為指向 ,AllNotesPage
而不是 NotePage
:
<?xml version="1.0" encoding="UTF-8" ?>
<Shell
x:Class="Notes.AppShell"
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:views="clr-namespace:Notes.Views"
Shell.FlyoutBehavior="Disabled">
<TabBar>
<ShellContent
Title="Notes"
ContentTemplate="{DataTemplate views:AllNotesPage}"
Icon="{OnPlatform 'icon_notes.png', iOS='icon_notes_ios.png', MacCatalyst='icon_notes_ios.png'}" />
<ShellContent
Title="About"
ContentTemplate="{DataTemplate views:AboutPage}"
Icon="{OnPlatform 'icon_about.png', iOS='icon_about_ios.png', MacCatalyst='icon_about_ios.png'}" />
</TabBar>
</Shell>
如果您現在執行應用程式,如果您按下 [新增 ] 按鈕,就會發現它當機,抱怨它無法瀏覽至 NotesPage
。 每個可以從另一個頁面巡覽至的頁面,都必須嚮導覽系統註冊。 AllNotesPage
和 AboutPage
頁面會在 中TabBar宣告,自動嚮導覽系統註冊。
NotesPage
精靈覽系統註冊 :
在 [方案總管] 窗格中,開啟AppShell.xaml.cs檔案。
將一行新增至註冊導覽路由的建構函式:
namespace Notes; public partial class AppShell : Shell { public AppShell() { InitializeComponent(); Routing.RegisterRoute(nameof(Views.NotePage), typeof(Views.NotePage)); } }
Routing.RegisterRoute
方法採用兩個參數:
- 第一個參數是您想要註冊之 URI 的字串名稱,在此情況下,解析的名稱為
"NotePage"
。 - 第二個參數是巡覽至 時
"NotePage"
要載入的頁面類型。
現在您可以執行您的應用程式。 請嘗試新增筆記、在筆記之間來回巡覽,以及刪除筆記。
探索本教學課程的程序代碼。 如果您要下載已完成項目的復本來比較您的程式代碼,請下載 此專案。
恭喜!
您已完成建立 .NET MAUI 應用程式教學課程!
下一步
在教學課程系列的下一個部分中,您將瞭解如何在專案中實作model-view-viewmodel (MVVM) 模式。
下列連結提供您在本教學課程中學到之一些概念的詳細資訊:
在這個區段有遇到問題嗎? 如果有,請提供意見反應,好讓我們可以改善這個區段。