教程:创建 .NET MAUI 应用 您所在的位置:网站首页 edge浏览器启动时为什么会打开两个网页页面 教程:创建 .NET MAUI 应用

教程:创建 .NET MAUI 应用

2023-05-05 20:47| 来源: 网络整理| 查看: 265

将数据绑定到 UI 并导航页面 剩余 25 分钟

本教程的这一部分介绍了视图、模型和应用内导航的概念。

在本教程的前面步骤中,你向项目添加了两个页面: 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 编辑器中将其打开。 它应类似于以下代码片段:

.NET 命名空间通过 XML 命名空间声明导入到 XAML 中。 在前面的 XAML 标记中,它是 xmlns:local="clr-namespace:Notes" 根元素中的 属性: 。 声明 XML 命名空间以在同一程序集中导入 .NET 命名空间的格式为:

xmlns:{XML namespace name}="clr-namespace:{.NET namespace}"

因此,前面的声明将 的 local XML 命名空间映射到 的 Notes.NET 命名空间。 通常的做法是将名称 local 映射到项目的根命名空间。

local删除 XML 命名空间并添加新命名空间。 此新 XML 命名空间将映射到 的 Notes.Views.NET 命名空间,因此将其 views命名为 。 声明应类似于以下属性: xmlns:views="clr-namespace:Notes.Views"。

属性 local 使用了 ShellContent.ContentTemplate XML 命名空间,将其更改为 views。 XAML 现在应如以下代码片段所示:

现在,你应该能够运行应用而不会出现任何编译器错误,并且一切应仍像以前一样工作。

定义模型

目前,模型是嵌入笔记和关于视图的数据。 我们将创建新类来表示该数据。 首先,用于表示便笺页数据的模型:

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

将类命名为 Note.cs ,然后按 Add。

打开 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 ,然后按 Add。

打开 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 文件。

将内容替换为以下代码片段:

让我们看一下更改后的行,这些行在上一个代码片段中突出显示:

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

此行将 Notes.Models .NET 命名空间映射到 models XML 命名空间。

的 BindingContextContentPage 属性设置为 类的 Note.Models.About 实例,使用 的 models:AboutXML 命名空间和 对象。 这是使用 属性元素语法 而不是 XML 属性设置的。

重要

到目前为止,已使用 XML 属性设置属性。 这非常适合简单值,例如 Label.FontSize 属性。 但是,如果属性值更复杂,则必须使用 属性元素语法 来创建 对象。 请考虑以下示例创建具有其 FontSize 属性集的标签:

可以使用属性元素语法设置相同的FontSize属性:

22

三 个控件的 Text 属性值已从硬编码字符串更改为绑定语法: {Binding PATH}。

{Binding} 语法在运行时处理,允许从绑定返回的值是动态的。 的 PATH 部分 {Binding PATH} 是要绑定到的属性路径。 属性来自当前控件的 BindingContext。 对于 控件, BindingContext 是未设置的。 上下文在控件未设置时从父级继承,在本例中,父对象设置上下文为根对象: 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 文件。

更改 添加 属性的 Text 控件。 将 属性绑定到 Text 属性: 项...” 在“ 添加新项 ”对话框中,在窗口左侧的模板列表中选择 “.NET MAUI ”。 接下来,选择 .NET MAUI ContentPage (XAML) 模板。 将文件命名为 AllNotesPage.xaml,然后选择“ 添加”。 在“解决方案资源管理器”窗格中,右键单击Models文件夹,然后选择“添加>类...” 将类命名为 AllNotes.cs ,然后按 Add。 编写 AllNotes 模型代码

新模型将表示显示多个笔记所需的数据。 此数据将是表示笔记集合的属性。 集合将是一个 ObservableCollection 专用集合。 当控件列出多个项(如 ListView)绑定到 时 ObservableCollection,两者协同工作,自动使项列表与集合保持同步。 如果列表添加项,则集合将更新。 如果集合添加项,则控件将自动更新为新项。

在“解决方案资源管理器”窗格中,打开 Models\AllNotes.cs 文件。

将代码替换为以下代码片段:

using System.Collections.ObjectModel; namespace Notes.Models; internal class AllNotes { public ObservableCollection Notes { get; set; } = new ObservableCollection(); 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 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.GetCreationTime(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 文件。

将代码替换为以下标记:

前面的 XAML 引入了一些新概念:

属性 ContentPage.ToolbarItems 包含 ToolbarItem。 此处定义的按钮通常显示在应用顶部,并沿页面标题显示。 不过,根据平台,它可能处于不同的位置。 按下其中一个按钮时,将 Clicked 引发事件,就像普通按钮一样。

属性 ToolbarItem.IconImageSource 将图标设置为在按钮上显示。 图标可以是项目定义的任何图像资源,但在本示例中使用了 FontImage 。 可以使用 FontImage 字体的单个字形作为图像。

控件 CollectionView 显示项的集合,在本例中,绑定到模型的 Notes 属性。 通过 和 CollectionView.ItemTemplate 属性设置CollectionView.ItemsLayout集合视图呈现每个项的方式。

对于集合中的每个项, 生成 CollectionView.ItemTemplate 声明的 XAML。 BindingContext该 XAML 的 将成为集合项本身,在本例中为每个单独的注释。 注释的模板使用两个标签,这些标签绑定到注释的 Text 和 Date 属性。

处理 CollectionView 在 SelectionChanged 选择集合视图中的项时引发的事件。

需要编写视图的代码隐藏以加载笔记并处理事件。

在“解决方案资源管理器”窗格中,打开“视图/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:

如果现在运行应用,则如果按“ 添加” 按钮,会注意到它崩溃,并抱怨它无法导航到 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" 要加载的页面的类型。

现在可以运行应用了。 尝试添加新笔记、在笔记之间来回导航以及删除笔记。

浏览本教程的此步骤的代码。



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有