DotNetCore Is AnyWhere.
前言
Visual Studio 2019 已经正式发布了,DotNetCore 3.0 的正式版也指日可待。在之前的版本中,作为一名基于微软生态的传统 WPF 程序员看着隔壁同学在开发 DotNetCore 网站时用着各种特性好生羡慕,想着巨硬啥时候能让客户端开发者也能尝尝甜头。
那么,现在是时候可以尝试一下了。
需要说明的一点的是,DotNetCore 3.0 虽然跨平台,但是基于此的 WPF 却是针对 Windows 特定平台的实现,并不能跨 Linux 和 MacOS 。
开发环境准备
要想开发 DotNetCore 版本的 WPF,首先需要确保我们的机器上已经安装了如下
- Visual Studio 2019 下载地址
需要安装的组件如下图所示
- DotNetCore 3.0 SDK 下载地址
直接默认安装即可。
全新的开发体验
在首次使用 VS2019 创建 DotNetCore 版本的 WPF 程序时,VS 可能会给你爆个如下图所示的错误:
按照错误提示即可解决该问题,如下图所示
接着选择 TOOLS -> Options,配置如下图所示
补充:VS2017 15.9.3 以上版本也是可以创建 DotNetCore 3.0 WPF 的项目模板,同样需要先设置,设置方法和上面叙述的是一样的。
在 16.1 版本之后的设置界面如下图所示:
Hello World
首先,我们可以通过 VS 创建一个基于 DotNetCore 的 项目模板,然后我们看一下与传统的 WPF 项目模板有什么区别。如下图所示,创建一个 WPF 项目
创建完成后,尝试编译运行(注:第一次编译可能需要较长时间),如下图所示
如果我们仔细看一下这个新版的项目模板,会发现与传统的项目模板相比,有好几处发生了改变:
- **.csproj 的组织方式发生了改变,与传统的组织方式相比,内容精简的快没有了;
- 项目默认会引用 Microsoft.NETCore.Platforms 和 Microsoft.WindowsDesktop.App,这两个 Package 都是针对 WinForm 和 WPF 的特定包
- 项目属性中也有一些改动
- 生成目录中也有改动,会生成一些以 json 结尾的文件
上述这些改动都是最直观的改动,但是这些改动貌似不痛不痒,并不能吸引传统 WPF 开发者投入使用。接触过 DotNetCore Web 方向的开发者已经对里面的 DI,HttpClientFactory,EFCore 等使用的炉火纯青,那我们能不能在 WPF 中也使用这些东西呢?答案是必须要能啊,所有我们还需要探索一下它的一些硬核功能。下面列举几个我目前知道的几个我觉得很炫酷的功能。
使用 DI 和 Service Provider
能够使用 DI 和 Service Provider,这是我觉得最值得说一下的,因为它的使用方式简直和在 DotNetCore Web 里面的一摸一样,值得一说。
首先,我们创建一个 DotNetCore 版本的 WPF 项目,然后引用如下包:
- Microsoft.Extensions.DependencyInjection
- Microsoft.Extensions.Options.ConfigurationExtensions
- Microsoft.Extensions.Configuration.FileExtensions
- Microsoft.Extensions.Configuration.Json
注:上述包都需要安装 预览版本的 3.0.0 版本的才行
然后,在我们的项目根目录下创建一个 appsettings.json 文件,文件内容如下所示:
{
"AppSettings": {
"StringSetting": "Value",
"IntegerSetting": 42,
"BooleanSetting": true
}
}
然后将该文件的 Build Action 设置为 Content,Copy To Output Directiory 设置为 Copy if newer。
接着,我们在项目根目录下创建一个 AppSettings.cs 文件,用于隐射上面的 appsettings.json 文件,示例代码如下所示:
public class AppSettings
{
public string StringSetting { get; set; }
public int IntegerSetting { get; set; }
public bool BooleanSetting { get; set; }
}
然后,我们创建一个自定义的接口服务 ISampleService和对应实现 SampleService,示例代码如下所示:
public interface ISampleService
{
string GetCurrentDate();
}
public class SampleService : ISampleService
{
public string GetCurrentDate() => DateTime.Now.ToLongDateString();
}
然后,修改我们的 App.xaml 文件,删除掉默认添加的启动视图代码 StartupUri="MainWindow.xaml";
接着,修改我们的 App.xaml.cs 文件,示例代码如下所示:
public partial class App : Application
{
public IServiceProvider ServiceProvider { get; private set; }
public IConfiguration Configuration { get; private set; }
protected override void OnStartup(StartupEventArgs e)
{
// 初始化配置建造器
var builder = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true);
// 获取配置建造器创建的对象
Configuration = builder.Build();
//配置全局服务容器
var serviceCollection = new ServiceCollection();
ConfigureServices(serviceCollection);
ServiceProvider = serviceCollection.BuildServiceProvider();
var mainWindow = ServiceProvider.GetRequiredService<MainWindow>();
mainWindow.Show();
}
private void ConfigureServices(IServiceCollection services)
{
// 向全局容器中注册一个视图
services.AddTransient(