添加链接
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> 是的唯一子物件 ContentPageContentPage 只能有一個子物件。 此 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 頂端的播放按鈕來執行應用程式:

    建立附注的頁面

    現在,應用程式包含 MainPageAboutPage,您可以開始建立應用程式的其餘部分。 首先,您將建立一個頁面,讓使用者建立和顯示附註,然後撰寫程式代碼來載入並儲存筆記。

    記事頁面會顯示附註,並允許您儲存或刪除它。 首先,將新頁面新增至專案:

  • 在 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並瀏覽頁面

    本教學課程的這個部分介紹檢視、模型和應用程式內導覽的概念。

    在教學課程的先前步驟中,您已將兩個頁面新增至專案: NotePageAboutPage。 頁面代表數據的檢視。 NotePage是顯示「記事數據」的「檢視」,而 AboutPage 是顯示「應用程式資訊數據」的「檢視」。這兩個檢視都有該數據硬式編碼或內嵌的模型,您必須將數據模型與檢視區隔開。

    將模型與檢視區隔的優點為何? 它可讓您設計檢視來表示和與模型的任何部分互動,而不必擔心實作模型的實際程序代碼。 這是使用數據系結完成的,本教學課程稍後將會呈現。 不過,現在,讓我們重新建構專案。

    分隔檢視和模型

    重構現有的程序代碼,以將模型與檢視區隔開。 接下來的幾個步驟會組織程序代碼,讓檢視和模型彼此分開定義。

  • 從專案中,刪除 MainPage.xamlMainPage.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.xamlAboutPage.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);
    

    如果您查看反白顯示的這一行,程式代碼會檢查 是否 BindingContextModels.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.ItemsLayoutCollectionView.ItemTemplate 屬性設定每個專案的方式。

    針對集合中的每個專案,會產生 CollectionView.ItemTemplate 宣告的 XAML。 BindingContext該 XAML 的 會變成集合專案本身,在此案例中,每個個別的附註。 附註的範本會使用兩個標籤,這些標籤系結至附註的 TextDate 屬性。

  • 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 ,並使用其資訊來巡覽至 NotePageGoToAsync 會使用 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。 每個可以從另一個頁面巡覽至的頁面,都必須嚮導覽系統註冊。 AllNotesPageAboutPage 頁面會在 中TabBar宣告,自動嚮導覽系統註冊。

    NotesPage精靈覽系統註冊 :

  • [方案總管] 窗格中,開啟AppShell.xaml.cs檔案。

  • 將一行新增至註冊導覽路由的建構函式:

    namespace Notes;
    public partial class AppShell : Shell
        public AppShell()
            InitializeComponent();
            Routing.RegisterRoute(nameof(Views.NotePage), typeof(Views.NotePage));
    
  •