Codon 入门 - 第 1 部分






4.85/5 (15投票s)
Codon 是一个零依赖的跨平台 MVVM 框架,用于创建 UWP、WPF 和 Xamarin 应用程序。
Codon 入门 Part 1
视图模型初始化、属性和命令简介
此处提供的代码位于 GitHub 上 Samples repository 中的 Sample001。
背景
七年前,我创建了 Calcium 框架,它碰巧是最早的 MVVM 框架之一。我用它构建了许多桌面和移动应用程序,包括适用于 Android 和 Windows Phone 的 Surfy Browser。
随着时间的推移,Calcium 已发展成一套相当庞大的库。其中一些专注于 WPF 的旧代码已不再相关,并且有几个依赖项已悄然引入代码库。
几周前,我决定是时候进行一次大修了。经过大量的工程努力,新的代码库和库结构发生了巨大的变化,以至于我决定需要新的托管和新的名称。因此,隆重推出 Codon:一个基于 .NET Standard 的框架,它提供两套库:一个轻量级的 Essentials 包和一个供那些想要更多功能的 Extras 包。 Codon 存储库位于 GitHub 上。
构建可维护应用程序的框架
Codon 包含两个主要库:一个极简的 Essentials 包和一个 Extras 包。Codon.Essentials 包含以下功能:
- 易于使用的 ICommand 实现
- 无摩擦的 INPC
- 跨平台对话框服务
- 跨平台设置服务
- IoC 和 DI
- 日志支持
- 以及一个弱引用发布/订阅消息器
要安装 Codon 的 Essentials 包,请在 Visual Studio 的 Package Manager Console 中运行以下命令:
PM> Install-Package Codon.Essentials
Codon Extras 包含以下功能:
- 用户选项系统
- 表单输入验证
- 应用程序状态保存
- 各种跨平台启动器,用于共享链接、发送电子邮件等
要安装 Codon 的 Extras 包,请在 Visual Studio 的 Package Manager Console 中运行以下命令:
PM> Install-Package Codon.Extras
除了 Codon.Extras 包外,Codon 还有一个补充的数据绑定包 Codon.UI.Data,用于非 XAML 技术,如 Xamarin.Android 和 Xamarin.iOS;以及一个位于 Codon.UndoModel 包中的撤销/重做系统。Codon.UndoModel 可作为独立包下载。
.NET Standard 的迁移
在创建跨平台应用程序时,业务逻辑应该放在哪里这个问题很快就会出现。过去,答案是 PCL、共享项目或共享链接文件。这些选项都不能提供无摩擦的开发体验。幸运的是,现在有了第四种选择:.NET Standard 库项目。.NET Standard 允许您使用 .NET 中的大多数 API,并且根据 Microsoft 的说法,.NET Standard 将取代 PCL。如果您对 .NET Standard 感兴趣,可以 在此处 阅读更多相关信息。
顺便说一句,您 **不必** 理解 .NET Standard 即可使用 Codon。您也不必在项目中使用 .NET Standard。Codon 非常乐意作为您“原生”项目的引用。
设置您的解决方案
要创建 .NET Standard 类库,请在 Visual Studio 2017 中选择“Add new project”,然后从项目类型列表中选择 .NET Standard。
要添加对 Codon 的 NuGet 包的引用,可以使用上面解释的程序包管理器控制台,或者右键单击项目节点并选择“Manage NuGet Packages”。在“Browse”选项卡的搜索框中输入 Codon。您将看到列出了一堆 Codon 包。选择“Codon”,它是一个包含 Codon 核心功能的 .NET Standard 库。
通过对 Codon 的 NuGet 引用,您可以开始为您的应用程序创建视图模型。
探索示例代码
在基于 Codon 的应用程序中,视图模型通常继承自其中一个视图模型基类。在示例中,Page1ViewModel
继承自 ` Codon.UIModel.ViewModelBase`。
创建绑定友好的属性
Page1ViewModel
包含一个名为 Foo
的属性,它演示了如何创建一个属性,该属性在设置值时自动引发 PropertyChanging
和 PropertyChanged
。参见列表 1。
get 和 set 访问器使用 C# 7 的表达式语法实现。
UWP、WPF 和 Android 中的 UI 元素具有与 UI 线程的线程亲和性。也就是说,所有影响 UI 元素状态的更改都必须在 UI 线程上执行。这就是 Codon 的 ViewModelBase
发挥作用的地方,因为基类的 Set
方法会负责确保属性更改事件始终在 UI 线程上引发。
列表 1. 使用 Set 方法。
string foo;
public string Foo
{
get => foo;
set => Set(ref foo, value);
}
如果您需要检查值是否确实被设置,Set
方法会返回一个 AssignmentResult
枚举值,它可以是以下之一:
- AlreadyAssigned:表示字段已设置为指定值。
- Cancelled:表示类 PropertyChanging 事件的订阅者决定取消更新。
- OwnerDisposed:表示视图模型已被释放。
- Success:表示字段已设置为指定值。
使用依赖注入初始化视图模型
Codon 的默认 IoC 容器支持依赖注入。如果我们请求 Page1ViewModel
的实例,则生成的对象会自动填充对话框服务、导航服务和设置服务。参见列表 2。我们稍后将探讨这些服务。
Page1ViewModel
使用 C# 7 的新松弛 throw 语句,并将其与 null 合并运算符结合使用,以确保没有任何参数为 null。
列表 2. Page1ViewModel 构造函数。
public Page1ViewModel(
IDialogService dialogService,
INavigationService navigationService,
ISettingsService settingsService)
{
this.dialogService = dialogService
?? throw new ArgumentNullException(nameof(dialogService));
this.navigationService = navigationService
?? throw new ArgumentNullException(nameof(navigationService));
this.settingsService = settingsService
?? throw new ArgumentNullException(nameof(settingsService));
UpdateCreationCount();
}
在 XAML 应用程序中,ICommands
是将 UI 元素(如按钮)连接到应用程序业务逻辑的常用方法。ICommands
封装了当例如按钮被点击时需要执行的操作。在 Codon 中,主要的 ICommand
实现是 ActionCommand
类。 ActionCommand
必须使用一个 Action
进行初始化,该 Action
在 ActionCommand
执行时被调用。
Page1ViewModel
包含多个命令。让我们看一下 ShowDialogCommand
,其声明如下:
ActionCommand showDialogCommand;
public ICommand ShowDialogCommand => showDialogCommand
?? (showDialogCommand = new ActionCommand(ShowDialog));
该属性对 getter 和 setter 访问器使用 lambda 表达式。这是一种表达简单属性的简洁方式。该命令是惰性实例化的,这意味着当第一次检索该属性时,将创建 ActionCommand
。
注意: ActionCommand
类还允许您提供一个 Func
来确定 ICommand
是否可执行。
顺便说一句,Codon 中还有 ActionCommand
的其他变体,包括 UICommand
,它具有各种属性,如 Text
和 Visible
。此外,UICompositeCommand
允许您将多个命令绑定到单个 UI 元素。Extras 包还提供了对异步命令的支持,如果您需要的话。然而,高级命令超出了本文的范围。
当调用 ICommand.Execute
方法时,将调用 ShowDialog
方法。
ShowDialog
使用 IDialogService.ShowMessageAsync
方法向用户显示消息框,如下所示:
void ShowDialog(object arg)
{
dialogService.ShowMessageAsync(
"This is a sample message.", "Message from Sample 1");
}
为每个支持的平台都存在一个 IDialogService
实现。对话框服务不仅可用于显示消息或警告,还可用于显示通知并向用户询问文本响应问题。我们将在以后的文章中对此进行探讨。
在我们继续检查设置和服务之前,让我们先看看视图模型是如何绑定到视图的。
在示例中,我们有四个项目。第一个我们已经看过了:一个 .NET Standard 库。另外三个包括一个 Xamarin Android 应用项目、一个 WPF 应用项目和一个 UWP 应用项目。
这三个都遵循相同的模式。它们每个都包含与 .NET Standard 项目中的视图模型耦合的页面(或者在 Android 项目中是 activity)。
让我们先看看 UWP 项目。
Sample1.Uwp 项目引用 Codon.Uwp NuGet 包。Codon.Uwp 包通过 UWP 平台特定的 IDialogService
实现来补充 Codon 包。
项目中的每个页面都使用 Dependency
类从 IoC 容器检索其相应的视图模型。参见列表 3。
在 UWP 应用中,我们将视图模型声明为视图的一个属性。这允许使用 x:Bind
标记扩展,我们稍后会看到。
专业技巧 您可能想知道 Codon 如何知道 IDialogService
、ISettingsService
或 INavigationService
的实现。Codon 的 IoC 容器 FrameworkContainer
支持声明式类型关联。因此,不需要引导程序。如果您查看任何服务,您会看到它们被 DefaultTypeNameAttribute
和/或 DefaultTypeAttrubute
装饰,这告诉容器在哪里找到实现。此外,您可以使用内置的 System.ComponentModel.DefaultValue
属性来指定默认实现,而无需引用 Codon 核心 .NET Standard 库。
您可以通过使用 Dependency.Register
方法注册类型关联来覆盖默认类型关联。
列表 3. Sample1.Uwp Page1 类摘录。
public sealed partial class Page1 : Page
{
public Page1()
{
/* Using the Dependency class to resolve
* the view-model automatically causes it to receive
* the services it needs via dependency injection. */
DataContext = Dependency.Resolve<Page1ViewModel>();
InitializeComponent();
}
public Page1ViewModel ViewModel => (Page1ViewModel)DataContext;
}
Page1.xaml 包含各种绑定到视图模型命令的按钮。以下摘录显示了视图如何绑定到视图模型的 ShowDialogCommand
。
<Button Command="{x:Bind ViewModel.ShowDialogCommand}">Show Message 1</Button>
当用户激活“显示消息 1”按钮时,将显示一个对话框。参见图 1。
图 1. 对话框向用户显示消息。
WPF 版本的应用程序看起来很相似。它绑定到视图模型的 ShowDialogCommand
,如下所示:
<Button Command="{Binding ShowDialogCommand}">Show Message</Button>
Xamarin Android 没有内置的绑定基础结构。因此,您可能会认为我们需要做一些完全不同的事情来启用数据绑定。并非如此。Codon 支持布局文件绑定,正如Sample1.Android 项目的Page1.axml 布局文件中的这个摘录所示:
<Button l:Binding="Target=Click, Path=ShowDialogCommand"
android:id="@+id/Page1_Button_ShowDialog"
android:text="Show Dialog" />
注意: 布局文件绑定要求 Android 项目引用 Codon.Extras 包。
结论
在本文中,您了解了如何使用 Codon 创建一个简单的跨平台应用程序。您了解了如何创建一个 .NET Standard 库来包含应用程序的 UI 和业务逻辑。您探索了如何通过继承 ViewModelBase
类来创建视图模型。然后,您了解了如何使用 Codon 的核心服务之一:对话框服务,向用户显示消息。在 下一部分 中,我们将回到 .NET Standard 项目,进一步探讨视图模型逻辑,并了解如何使用 Codon 的设置服务。
我希望这个项目对您有用。如果觉得有用,请评分和/或在下方留言。
历史
2017年4月2日
- 首次发布