在本快速入门中,你将了解如何:
- 向 Xamarin.Forms Shell 应用程序添加更多页面。
- 在页面之间执行导航。
- 使用数据绑定在用户界面元素与其数据源之间同步数据。
本快速入门演练如何将能够存储单个便笺的跨平台 Xamarin.Forms Shell 应用程序转换为能够存储多个便笺的应用程序。 最终的应用程序如下所示:
先决条件
在尝试本快速入门之前,应成功完成上一个快速入门。
使用 Visual Studio 更新应用
启动 Visual Studio。 在开始窗口中,单击最近使用的项目/解决方案列表中的“Notes”解决方案,或单击“打开项目或解决方案”,然后在“打开项目/解决方案”对话框中,选择“Notes”项目的解决方案文件:

在“解决方案资源管理器”中,右键单击“Notes”项目,并依次选择“添加”>“新文件夹”:

在“解决方案资源管理器”中,将新文件夹命名为“Models”:

在“解决方案资源管理器”中选择“Models”文件夹,右键单击,然后选择“添加”>“类...”:

在“添加新项”对话框中,选择“Visual C# 项”>“类”,将新文件命名为“Note”,然后单击“添加”按钮:

这会将名为 Note 的类添加到“Notes”项目的“Models”文件夹中。
在 Note.cs 中,删除所有模板代码并将其替换为以下代码:
using System; namespace Notes.Models { public class Note { public string Filename { get; set; } public string Text { get; set; } public DateTime Date { get; set; } } }此类定义一个
Note模型,该模型将在应用程序中存储有关每个便笺的数据。通过按 Ctrl+S,保存对 Note.cs 所做的更改。
在“解决方案资源管理器”的“Notes”项目中,选择“Views”文件夹,右键单击,然后选择“添加”>“新建项...”。在“添加新项”对话框中,选择“Visual C# 项”>“Xamarin.Forms”>“内容页”,将新文件命名为“NoteEntryPage”,然后单击“添加”按钮:

这会将名为“NoteEntryPage”的新页添加到项目的“Views”文件夹中。 此页将用于便笺输入。
在 NoteEntryPage.xaml 中,删除所有模板代码并将其替换为以下代码:
<?xml version="1.0" encoding="UTF-8"?> <ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="Notes.Views.NoteEntryPage" Title="Note Entry"> <!-- Layout children vertically --> <StackLayout Margin="20"> <Editor Placeholder="Enter your note" Text="{Binding Text}" HeightRequest="100" /> <!-- Layout children in two columns --> <Grid ColumnDefinitions="*,*"> <Button Text="Save" Clicked="OnSaveButtonClicked" /> <Button Grid.Column="1" Text="Delete" Clicked="OnDeleteButtonClicked"/> </Grid> </StackLayout> </ContentPage>该代码以声明的方式定义页面的用户界面,它包含一个用于文本输入的
Editor,以及两个指示应用程序保存或删除文件的Button对象。 这两个Button实例水平放置在Grid中,而Editor和Grid垂直放置在StackLayout中。 此外,Editor使用数据绑定来绑定到Note模型的Text属性。 有关数据绑定的详细信息,请参阅 Xamarin.Forms 快速入门深入探讨中的数据绑定。通过按 Ctrl+S,保存对 NoteEntryPage.xaml 所做的更改。
在 NoteEntryPage.xaml.cs 中,删除所有模板代码并将其替换为以下代码:
using System; using System.IO; using Notes.Models; using Xamarin.Forms; namespace Notes.Views { [QueryProperty(nameof(ItemId), nameof(ItemId))] public partial class NoteEntryPage : ContentPage { public string ItemId { set { LoadNote(value); } } public NoteEntryPage() { InitializeComponent(); // Set the BindingContext of the page to a new Note. BindingContext = new Note(); } void LoadNote(string filename) { try { // Retrieve the note and set it as the BindingContext of the page. Note note = new Note { Filename = filename, Text = File.ReadAllText(filename), Date = File.GetCreationTime(filename) }; BindingContext = note; } catch (Exception) { Console.WriteLine("Failed to load note."); } } async void OnSaveButtonClicked(object sender, EventArgs e) { var note = (Note)BindingContext; if (string.IsNullOrWhiteSpace(note.Filename)) { // Save the file. var filename = Path.Combine(App.FolderPath, $"{Path.GetRandomFileName()}.notes.txt"); File.WriteAllText(filename, note.Text); } else { // Update the file. File.WriteAllText(note.Filename, note.Text); } // Navigate backwards await Shell.Current.GoToAsync(".."); } async void OnDeleteButtonClicked(object sender, EventArgs e) { var note = (Note)BindingContext; // Delete the file. if (File.Exists(note.Filename)) { File.Delete(note.Filename); } // Navigate backwards await Shell.Current.GoToAsync(".."); } } }此代码将
Note实例(表示单个便笺)存储在页面的BindingContext中。 该类用QueryPropertyAttribute进行修饰,在导航过程中,可以通过查询参数将数据传入页面。QueryPropertyAttribute的第一个参数指定将接收数据的属性的名称,第二个参数指定查询参数 ID。因此,上述代码中的QueryParameterAttribute指定ItemId属性将接收从GoToAsync方法调用中指定的 URI 传入ItemId查询参数的数据。 然后,ItemId属性调用LoadNote方法,从设备上的文件中创建一个Note对象,并将页面的BindingContext设置为Note对象。按“保存”
Button时,会执行OnSaveButtonClicked事件处理程序,它会将Editor的内容保存到具有随机生成的文件名的新文件,或者保存到现有文件(如果正在更新便笺)。 在这两种情况下,文件都存储在应用程序的本地应用程序数据文件夹中。 然后该方法会导航回上一页。 按“删除”Button时,会执行OnDeleteButtonClicked事件处理程序,它会删除文件(前提是它存在)并导航回上一页。 有关导航的详细信息,请参阅 Xamarin.Forms Shell 快速入门深入探讨中的导航。通过按 Ctrl+S,保存对 NoteEntryPage.xaml.cs 所做的更改。
警告
由于在后续步骤中将修复一些错误,暂时不会生成应用程序。
在“解决方案资源管理器”的“Notes”项目中,打开“Views”文件夹中的“NotesPage.xaml” 。
在 NotesPage.xaml 中,删除所有模板代码并将其替换为以下代码:
<?xml version="1.0" encoding="UTF-8"?> <ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="Notes.Views.NotesPage" Title="Notes"> <!-- Add an item to the toolbar --> <ContentPage.ToolbarItems> <ToolbarItem Text="Add" Clicked="OnAddClicked" /> </ContentPage.ToolbarItems> <!-- Display notes in a list --> <CollectionView x:Name="collectionView" Margin="20" SelectionMode="Single" SelectionChanged="OnSelectionChanged"> <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="Medium"/> <Label Text="{Binding Date}" TextColor="Silver" FontSize="Small" /> </StackLayout> </DataTemplate> </CollectionView.ItemTemplate> </CollectionView> </ContentPage>此代码以声明方式定义页面的用户界面,该界面由
CollectionView和ToolbarItem组成。CollectionView使用数据绑定来显示应用程序检索到的任何便笺。 选择便笺会导航到NoteEntryPage,在那里可以修改便笺。 或者,可以通过按ToolbarItem来创建新便笺。 有关数据绑定的详细信息,请参阅 Xamarin.Forms 快速入门深入探讨中的数据绑定。通过按 Ctrl+S,保存对 NotesPage.xaml 所做的更改。
在“解决方案资源管理器”的“Notes”项目中,展开“Views”文件夹中的“NotesPage.xaml”,并打开“NotesPage.xaml.cs” 。
在 NotesPage.xaml.cs 中,删除所有模板代码并将其替换为以下代码:
using System; using System.Collections.Generic; using System.IO; using System.Linq; using Notes.Models; using Xamarin.Forms; namespace Notes.Views { public partial class NotesPage : ContentPage { public NotesPage() { InitializeComponent(); } protected override void OnAppearing() { base.OnAppearing(); var notes = new List<Note>(); // Create a Note object from each file. var files = Directory.EnumerateFiles(App.FolderPath, "*.notes.txt"); foreach (var filename in files) { notes.Add(new Note { Filename = filename, Text = File.ReadAllText(filename), Date = File.GetCreationTime(filename) }); } // Set the data source for the CollectionView to a // sorted collection of notes. collectionView.ItemsSource = notes .OrderBy(d => d.Date) .ToList(); } async void OnAddClicked(object sender, EventArgs e) { // Navigate to the NoteEntryPage, without passing any data. await Shell.Current.GoToAsync(nameof(NoteEntryPage)); } async void OnSelectionChanged(object sender, SelectionChangedEventArgs e) { if (e.CurrentSelection != null) { // Navigate to the NoteEntryPage, passing the filename as a query parameter. Note note = (Note)e.CurrentSelection.FirstOrDefault(); await Shell.Current.GoToAsync($"{nameof(NoteEntryPage)}?{nameof(NoteEntryPage.ItemId)}={note.Filename}"); } } } }此代码定义
NotesPage的功能。 显示该页面时,会执行OnAppearing方法,该方法使用从本地应用程序数据文件夹中检索到的任何便笺填充CollectionView。 按ToolbarItem时,会执行OnAddClicked事件处理程序。 此方法导航到NoteEntryPage。 选择CollectionView中的项时,会执行OnSelectionChanged事件处理程序。 此方法导航到NoteEntryPage,前提是选中了CollectionView中的一个项,将选中的Note的Filename属性作为查询参数传递给页面。 有关导航的详细信息,请参阅 Xamarin.Forms 快速入门深入探讨中的导航。通过按 Ctrl+S,保存对 NotesPage.xaml.cs 所做的更改。
警告
由于在后续步骤中将修复一些错误,暂时不会生成应用程序。
在“解决方案资源管理器”的“Notes”项目中,展开“AppShell.xaml”,然后打开“AppShell.xaml.cs” 。 然后将现有代码替换为以下代码:
using Notes.Views; using Xamarin.Forms; namespace Notes { public partial class AppShell : Shell { public AppShell() { InitializeComponent(); Routing.RegisterRoute(nameof(NoteEntryPage), typeof(NoteEntryPage)); } } }此代码为在 Shell 视觉层次结构 (AppShell.xaml) 中未表示的
NoteEntryPage注册了一个路由。 然后,可以使用GoToAsync方法导航此页以使用基于 URI 的导航。通过按 Ctrl+S,保存对 AppShell.xaml.cs 所做的更改。
在“解决方案资源管理器”的“Notes”项目中,展开“App.xaml”,然后打开“App.xaml.cs” 。 然后将现有代码替换为以下代码:
using System; using System.IO; using Xamarin.Forms; namespace Notes { public partial class App : Application { public static string FolderPath { get; private set; } public App() { InitializeComponent(); FolderPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData)); MainPage = new AppShell(); } protected override void OnStart() { } protected override void OnSleep() { } protected override void OnResume() { } } }此代码为
System.IO命名空间添加命名空间声明,并为类型为string的静态FolderPath属性添加声明。FolderPath属性用于存储在设备上存储便笺数据的路径。 此外,该代码会在App构造函数中初始化FolderPath属性,并将MainPage属性初始化为已设置子类的Shell对象。通过按 Ctrl+S,保存对 App.xaml.cs 所做的更改。
在每个平台上生成并运行项目。 有关详细信息,请参阅生成快速入门。
在 NotesPage 上,按“添加” 按钮以导航到 NoteEntryPage 并输入便笺。 保存便笺之后,应用程序会导航回 NotesPage。
输入一些不同长度的便笺,以观察应用程序行为。 关闭应用程序并重新启动它,以确保你输入的笔记已保存到设备。
使用 Visual Studio for Mac 更新应用
启动 Visual Studio for Mac。 在开始窗口单击“打开”,然后在对话框中选择“Notes”项目的解决方案文件:

在“Solution Pad”中,右键单击“Notes”项目,并依次选择“添加”>“新建文件夹”:

在“新建文件夹”对话框中,将新文件夹命名为“Models”:

在“Solution Pad”中选择“Models”文件夹,右键单击,然后选择“添加”>“新建类...”:

在“新建文件”对话框中,选择“常规”>“空类”,将新文件命名为“Note”,然后单击“新建”按钮:

这会将名为 Note 的类添加到“Notes”项目的“Models”文件夹中。
在 Note.cs 中,删除所有模板代码并将其替换为以下代码:
using System; namespace Notes.Models { public class Note { public string Filename { get; set; } public string Text { get; set; } public DateTime Date { get; set; } } }此类定义一个
Note模型,该模型将在应用程序中存储有关每个便笺的数据。通过选择“文件”>“保存”(或按 ⌘ + S),保存对 Note.cs 所做的更改。
在“Solution Pad”中,选择“Notes”项目,然后右键单击并选择“添加”>“新建文件...”。在“新建文件”对话框中,选择“窗体”>“窗体 ContentPage Xaml”,将新文件命名为“NoteEntryPage”,然后单击“新建”按钮:

这会将名为“NoteEntryPage”的新页添加到项目的“Views”文件夹中。 此页将用于便笺输入。
在 NoteEntryPage.xaml 中,删除所有模板代码并将其替换为以下代码:
<?xml version="1.0" encoding="UTF-8"?> <ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="Notes.Views.NoteEntryPage" Title="Note Entry"> <!-- Layout children vertically --> <StackLayout Margin="20"> <Editor Placeholder="Enter your note" Text="{Binding Text}" HeightRequest="100" /> <!-- Layout children in two columns --> <Grid ColumnDefinitions="*,*"> <Button Text="Save" Clicked="OnSaveButtonClicked" /> <Button Grid.Column="1" Text="Delete" Clicked="OnDeleteButtonClicked"/> </Grid> </StackLayout> </ContentPage>该代码以声明的方式定义页面的用户界面,它包含一个用于文本输入的
Editor,以及两个指示应用程序保存或删除文件的Button对象。 这两个Button实例水平放置在Grid中,而Editor和Grid垂直放置在StackLayout中。 此外,Editor使用数据绑定来绑定到Note模型的Text属性。 有关数据绑定的详细信息,请参阅 Xamarin.Forms 快速入门深入探讨中的数据绑定。通过选择“文件”>“保存”(或按 ⌘ + S),保存对 NoteEntryPage.xaml 所做的更改。
在 NoteEntryPage.xaml.cs 中,删除所有模板代码并将其替换为以下代码:
using System; using System.IO; using Notes.Models; using Xamarin.Forms; namespace Notes.Views { [QueryProperty(nameof(ItemId), nameof(ItemId))] public partial class NoteEntryPage : ContentPage { public string ItemId { set { LoadNote(value); } } public NoteEntryPage() { InitializeComponent(); // Set the BindingContext of the page to a new Note. BindingContext = new Note(); } void LoadNote(string filename) { try { // Retrieve the note and set it as the BindingContext of the page. Note note = new Note { Filename = filename, Text = File.ReadAllText(filename), Date = File.GetCreationTime(filename) }; BindingContext = note; } catch (Exception) { Console.WriteLine("Failed to load note."); } } async void OnSaveButtonClicked(object sender, EventArgs e) { var note = (Note)BindingContext; if (string.IsNullOrWhiteSpace(note.Filename)) { // Save the file. var filename = Path.Combine(App.FolderPath, $"{Path.GetRandomFileName()}.notes.txt"); File.WriteAllText(filename, note.Text); } else { // Update the file. File.WriteAllText(note.Filename, note.Text); } // Navigate backwards await Shell.Current.GoToAsync(".."); } async void OnDeleteButtonClicked(object sender, EventArgs e) { var note = (Note)BindingContext; // Delete the file. if (File.Exists(note.Filename)) { File.Delete(note.Filename); } // Navigate backwards await Shell.Current.GoToAsync(".."); } } }此代码将
Note实例(表示单个便笺)存储在页面的BindingContext中。 该类用QueryPropertyAttribute进行修饰,在导航过程中,可以通过查询参数将数据传入页面。QueryPropertyAttribute的第一个参数指定将接收数据的属性的名称,第二个参数指定查询参数 ID。因此,上述代码中的QueryParameterAttribute指定ItemId属性将接收从GoToAsync方法调用中指定的 URI 传入ItemId查询参数的数据。 然后,ItemId属性调用LoadNote方法,从设备上的文件中创建一个Note对象,并将页面的BindingContext设置为Note对象。按“保存”
Button时,会执行OnSaveButtonClicked事件处理程序,它会将Editor的内容保存到具有随机生成的文件名的新文件,或者保存到现有文件(如果正在更新便笺)。 在这两种情况下,文件都存储在应用程序的本地应用程序数据文件夹中。 然后该方法会导航回上一页。 按“删除”Button时,会执行OnDeleteButtonClicked事件处理程序,它会删除文件(前提是它存在)并导航回上一页。 有关导航的详细信息,请参阅 Xamarin.Forms Shell 快速入门深入探讨中的导航。通过选择“文件”>“保存”(或按 ⌘ + S),保存对 NoteEntryPage.xaml.cs 所做的更改。
警告
由于在后续步骤中将修复一些错误,暂时不会生成应用程序。
在“Solution Pad”的“Notes”项目中,打开“Views”文件夹中的“NotesPage.xaml” 。
在 NotesPage.xaml 中,删除所有模板代码并将其替换为以下代码:
<?xml version="1.0" encoding="UTF-8"?> <ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="Notes.Views.NotesPage" Title="Notes"> <!-- Add an item to the toolbar --> <ContentPage.ToolbarItems> <ToolbarItem Text="Add" Clicked="OnAddClicked" /> </ContentPage.ToolbarItems> <!-- Display notes in a list --> <CollectionView x:Name="collectionView" Margin="20" SelectionMode="Single" SelectionChanged="OnSelectionChanged"> <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="Medium"/> <Label Text="{Binding Date}" TextColor="Silver" FontSize="Small" /> </StackLayout> </DataTemplate> </CollectionView.ItemTemplate> </CollectionView> </ContentPage>此代码以声明方式定义页面的用户界面,该界面由
CollectionView和ToolbarItem组成。CollectionView使用数据绑定来显示应用程序检索到的任何便笺。 选择便笺会导航到NoteEntryPage,在那里可以修改便笺。 或者,可以通过按ToolbarItem来创建新便笺。 有关数据绑定的详细信息,请参阅 Xamarin.Forms 快速入门深入探讨中的数据绑定。通过选择“文件”>“保存”(或按 ⌘ + S),保存对 NotesPage.xaml 所做的更改。
在“Solution Pad”的“Notes”项目中,展开“Views”文件夹中的“NotesPage.xaml”,并打开“NotesPage.xaml.cs” 。
在 NotesPage.xaml.cs 中,删除所有模板代码并将其替换为以下代码:
using System; using System.Collections.Generic; using System.IO; using System.Linq; using Notes.Models; using Xamarin.Forms; namespace Notes.Views { public partial class NotesPage : ContentPage { public NotesPage() { InitializeComponent(); } protected override void OnAppearing() { base.OnAppearing(); var notes = new List<Note>(); // Create a Note object from each file. var files = Directory.EnumerateFiles(App.FolderPath, "*.notes.txt"); foreach (var filename in files) { notes.Add(new Note { Filename = filename, Text = File.ReadAllText(filename), Date = File.GetCreationTime(filename) }); } // Set the data source for the CollectionView to a // sorted collection of notes. collectionView.ItemsSource = notes .OrderBy(d => d.Date) .ToList(); } async void OnAddClicked(object sender, EventArgs e) { // Navigate to the NoteEntryPage, without passing any data. await Shell.Current.GoToAsync(nameof(NoteEntryPage)); } async void OnSelectionChanged(object sender, SelectionChangedEventArgs e) { if (e.CurrentSelection != null) { // Navigate to the NoteEntryPage, passing the filename as a query parameter. Note note = (Note)e.CurrentSelection.FirstOrDefault(); await Shell.Current.GoToAsync($"{nameof(NoteEntryPage)}?{nameof(NoteEntryPage.ItemId)}={note.Filename}"); } } } }此代码定义
NotesPage的功能。 显示该页面时,会执行OnAppearing方法,该方法使用从本地应用程序数据文件夹中检索到的任何便笺填充CollectionView。 按ToolbarItem时,会执行OnAddClicked事件处理程序。 此方法导航到NoteEntryPage。 选择CollectionView中的项时,会执行OnSelectionChanged事件处理程序。 此方法导航到NoteEntryPage,前提是选中了CollectionView中的一个项,将选中的Note的Filename属性作为查询参数传递给页面。 有关导航的详细信息,请参阅 Xamarin.Forms 快速入门深入探讨中的导航。通过选择“文件”>“保存”(或按 ⌘ + S),保存对 NotesPage.xaml.cs 所做的更改。
警告
由于在后续步骤中将修复一些错误,暂时不会生成应用程序。
在“Solution Pad”的“Notes”项目中,展开“AppShell.xaml”,然后打开“AppShell.xaml.cs” 。 然后将现有代码替换为以下代码:
using Notes.Views; using Xamarin.Forms; namespace Notes { public partial class AppShell : Shell { public AppShell() { InitializeComponent(); Routing.RegisterRoute(nameof(NoteEntryPage), typeof(NoteEntryPage)); } } }此代码为在 Shell 视觉层次结构中未表示的
NoteEntryPage注册了一个路由。 然后,可以使用GoToAsync方法导航此页以使用基于 URI 的导航。通过选择“文件”>“保存”(或按 ⌘ + S),保存对 AppShell.xaml.cs 所做的更改。
在“Solution Pad”的“Notes”项目中,展开“App.xaml”,然后打开“App.xaml.cs” 。 然后将现有代码替换为以下代码:
using System; using System.IO; using Xamarin.Forms; namespace Notes { public partial class App : Application { public static string FolderPath { get; private set; } public App() { InitializeComponent(); FolderPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData)); MainPage = new AppShell(); } protected override void OnStart() { } protected override void OnSleep() { } protected override void OnResume() { } } }此代码为
System.IO命名空间添加命名空间声明,并为类型为string的静态FolderPath属性添加声明。FolderPath属性用于存储在设备上存储便笺数据的路径。 此外,该代码会在App构造函数中初始化FolderPath属性,并将MainPage属性初始化为已设置子类的Shell对象。通过选择“文件”>“保存”(或按 ⌘ + S),保存对 App.xaml.cs 所做的更改。
在每个平台上生成并运行项目。 有关详细信息,请参阅生成快速入门。
在 NotesPage 上,按“添加” 按钮以导航到 NoteEntryPage 并输入便笺。 保存便笺之后,应用程序会导航回 NotesPage。
输入一些不同长度的便笺,以观察应用程序行为。 关闭应用程序并重新启动它,以确保你输入的笔记已保存到设备。
后续步骤
在此快速入门中,读者学习了如何:
- 向 Xamarin.Forms Shell 应用程序添加更多页面。
- 在页面之间执行导航。
- 使用数据绑定在用户界面元素与其数据源之间同步数据。
继续学习下一个快速入门教程,修改应用程序以便它将其数据存储在本地 SQLite.NET 数据库中。

