添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

本教學課程旨在示範如何建立僅使用跨平台程序代碼的 .NET 多平臺應用程式 UI (.NET MAUI) 應用程式。 也就是說,您撰寫的程式代碼不會專屬於 Windows、Android、iOS 或 macOS。 您將建立的應用程式將會是記事應用程式,用戶可以在其中建立、儲存及載入多個筆記。

在本教學課程中,您將瞭解如何:

  • 建立 .NET MAUI Shell 應用程式。
  • 在您選擇的平台上執行您的應用程式。
  • 使用 eXtensible Application Markup Language (XAML) 定義使用者介面,並透過程式代碼與 XAML 元素互動。
  • 建立檢視並將其系結至數據。
  • 使用導覽來往和移出頁面。
  • 您將使用 Visual Studio 2022 建立應用程式,以便輸入附註並將其儲存至裝置記憶體。 最後的應用程式如下所示:

    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] 專案 > [新增 > 新項目] 。

    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 或選取 [檔案來儲存盤案。

    請細分頁面上所放置 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 或選取 [檔案

    現在 XAML 和 程式代碼後置 AboutPage 已完成,您必須在應用程式中顯示它。

    新增映像資源

    某些控制項可以使用影像,以增強使用者與應用程式互動的方式。 在本節中,您將下載兩個您將在應用程式中使用的映像,以及兩個與iOS搭配使用的替代映像。

    下載下列影像:

    圖示:關於
    此影像會作為您稍早建立之關於頁面的圖示。

    圖示:附註
    此影像會作為您將在本教學課程下一個部分中建立的附註頁面圖示。

    圖示:關於 (iOS)

    圖示:附註(iOS)

    下載映射之後,您可以使用 檔案總管 將它們移至專案的 Resources\Images 資料夾。 此資料夾中的任何檔案都會自動包含在專案中作為 MauiImage 資源。 您也可以使用 Visual Studio 將影像新增至您的專案。 如果您手動移動影像,請略過下列程式。

    請勿略過下載 iOS 特定映射,必須完成本教學課程。

    使用 Visual Studio 移動影像

  • 在 Visual Studio 的 [方案總管] 窗格中,展開 [Resources] 資料夾,其中顯示 [Images] 資料夾。

    您可以使用 檔案總管 將影像直接拖放到 [影像] 資料夾頂端的 [方案總管] 窗格中。 這會自動將檔案移至資料夾,並將其包含在專案中。 如果您選擇拖放檔案,請忽略此程序的其餘部分。

  • 以滑鼠右鍵按兩下 [影像 ],然後選取 [ 新增>現有專案]。

  • 流覽至包含所下載影像的資料夾。

  • 將篩選條件更改為檔案類型篩選至 圖片檔案。

    按住 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 或選取 [檔案來儲存盤案。

    讓我們細分 XAML 的主要部分:

    <Shell> 是 XAML 標記的根物件。
  • <TabBar> 是的內容 Shell。
  • 內的<ShellContent>兩個 <TabBar> 物件。 在您取代範本程式代碼之前,有一個指向頁面的單 <ShellContent> 一物件 MainPage 。
  • TabBar及其子系不會代表任何使用者介面元素,而是代表應用程式視覺階層的組織。 Shell 會採用這些物件,併產生內容的使用者介面,頂端有代表每個頁面的列。 每個頁面的 ShellContent.Icon 屬性都會使用 OnPlatform 標記延伸。 這個 XAML 標記延伸是用來指定不同平臺的不同值。 在這裡範例中,每個平台預設都會使用 icon_about.png 圖示,但 iOS 和 MacCatalyst 會使用 icon_about_ios.png。

    每個 <ShellContent> 物件都指向要顯示的頁面。 這是由 ContentTemplate 屬性所設定。

    執行應用程式

    按 F5 或按下 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 或選取 [檔案來儲存盤案。

    請細分頁面上所放置 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 或選取 [檔案

    程序代碼後置檔案的最終程式代碼看起來應該如下所示:

    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,以顯示程式代碼後置檔案:

  • 針對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"
                 x:DataType="models:About">
        <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 命名空間。

  • x:DataType="models:About"

    這一行會指示 XAML 編譯程式編譯所有系結表達式以提升運行時間效能,並針對 Notes.Models.About 類型解析系結表達式。

  • 的 BindingContextContentPage 屬性會使用 的 XML 命名空間和 物件,設定為 類別的Note.Models.Aboutmodels:About實例。 這是使用 屬性項目語法 而不是 XML 屬性來設定。

    到目前為止,屬性已使用 XML 屬性來設定。 這適用於簡單的值,例如 Label.FontSize 屬性。 但是,如果屬性值比較複雜,您必須使用 屬性元素語法 來建立物件。 請考慮使用其 FontSize 屬性集建立標籤的下列範例:

    <Label FontSize="22" />
    

    您可以使用屬性元素語法FontSize相同的屬性:

    <Label>
        <Label.FontSize>
        </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 檔案。

  • 將內容取代為下列代碼段:

    <?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.NotePage"
                 Title="Note"
                 x:DataType="models: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>
    

    讓我們看看已變更的行,其反白顯示於上一個代碼段:

  • xmlns:models="clr-namespace:Notes.Models"

    這一行會將 Notes.Models .NET 命名空間對應至 models XML 命名空間。

  • x:DataType="models:Note"

    這一行會指示 XAML 編譯程式編譯所有系結表達式以提升運行時間效能,並針對 Notes.Models.Note 類型解析系結表達式。

  • Text="{Binding Text}"

    這一行會藉由新增 <Editor> 屬性,並將 屬性系結至 Text 屬性,來變更 Text 控件。

    程序代碼後置的修改比 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"
                 xmlns:models="clr-namespace:Notes.Models"
                 x:Class="Notes.Views.AllNotesPage"
                 Title="Your Notes"
                 x:DataType="models:AllNotes">
        <!-- 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 x:DataType="models:Note">
                    <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
    
  • 新增名為 string的新ItemId屬性。 這個屬性會呼叫 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));
    
  •