.NET Core 3.0 助力 WPF 开发

2019 年 4 月 7 日 DotNet

(给DotNet加星标,提升.Net技能


转自:hippieZhou

cnblogs.com/hippieZhou/p/10637348.html


前言


Visual Studio 2019 已经正式发布了,.NET Core 3.0的正式版也指日可待。


之前的版本中,作为一名基于微软生态的传统 WPF 程序员看着隔壁同学在开发

DotNetCore网站时用着各种特性好生羡慕,想着巨硬啥时候能让客户端开发者也能尝尝甜头。那么,现在是时候可以尝试一下了。


需要说明的一点的是,DotNetCore 3.0虽然跨平台,但是基于此的 WPF 却是针对 Windows 特定平台的实现,并不能跨 Linux 和 MacOS 。


开发环境准备


要想开发 DotNetCore 版本的 WPF,首先需要确保我们的机器上已经安装了如下


Visual Studio 2019 :https://visualstudio.microsoft.com/downloads/#2019rc


需要安装的组件如下图所示




DotNetCore 3.0 SDK:https://dotnet.microsoft.com/download/dotnet-core/3.0


直接默认安装即可。


全新的开发体验


在首次使用 VS2019 创建 DotNetCore 版本的 WPF 程序时,VS 可能会给你爆个如下图所示的错误:



按照错误提示即可解决该问题,如下图所示



接着选择 TOOLS -> Options,配置如下图所示



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, To Output Directiory 设置为  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(typeof(MainWindow));
// 在全局容器中配置 AppSettings
services.Configure<AppSettings>(Configuration.GetSection(nameof(AppSettings)));
// 在全局容器中注册自定义服务
services.AddScoped<ISampleService, SampleService>();
}
}


修改完毕后,我们可以尝试编译我们的项目,如果不出意外的啊,我们的程序会正常启动起来。这里就不做截图说明了。


看了上述代码,是不是觉得很有意思啊,这种全新的开发模式顿时把我们的代码水平提升了好几个档次。这种使用方式和在 AspDotNetCore 简直一摸一样。


比如,我们在上面注册了 AppSettings 和一个基于 ISampleService 接口的实现 SampleService,那么我们就可以在 MainWindow 的构造函数中使用,比如,我们可以参考下面的示例代码:


public partial class MainWindow : Window
{
private readonly ISampleService _sampleService;
private readonly IOptions<AppSettings> _settings;
public MainWindow(ISampleService sampleService, IOptions<AppSettings> settings)
{
InitializeComponent();
_sampleService = sampleService;
var val = _sampleService.GetCurrentDate();
_settings = settings;
}
private void ButtonExit_Click(object sender, RoutedEventArgs e)
{
Application.Current.Shutdown();
}
}


然后,我们可以监视一下 **_settings** 的值,如下图所示:



通过这个简单的例子,我们可以看到这种全新方式的依赖注入已经得到微软的大力支持,将基于 .NetCore 的 CS模式 和 BS模式 开发方式进行了统一,学习曲线是不是又下降了很多啊。


使用 HttpClientFactory


众所周知,HttpClient 在实际的使用场景中还是存在一些弊端,在 DotNetCore 的 Web 端中,很多同学用了 HttpClientFactory 如鱼得水,减少了很多不必要的麻烦。现在,我们同样可以将这一利器在 WPF 中使用。


我们新建一个基于 DotNetCore 3.0 的 WPF 项目,然后引入如下包:


  • Microsoft.Extensions.DependencyInjection


  • Microsoft.Extensions.Http


然后,修改我们的 App.xaml 文件,删除掉默认添加的启动视图代码 StartupUri="MainWindow.xaml",并修改 App.xaml.cs 文件,示例代码如下所示:


public partial class App : Application
{
public ServiceProvider ServiceProvider { get; private set; }
protected override void OnStartup(StartupEventArgs e)
{
var serviceCollection = new ServiceCollection();
ConfigureServices(serviceCollection);
ServiceProvider = serviceCollection.BuildServiceProvider();
var mainView = ServiceProvider.GetRequiredService<MainWindow>();
mainView.Show();
base.OnStartup(e);
}
private void ConfigureServices(ServiceCollection services)
{
services.AddHttpClient();
services.AddTransient(typeof(MainWindow));
}
}


最后,修改我们的 MainWindow.xaml.cs 文件,示例代码如下所示:


public partial class MainWindow : Window
{
private readonly IHttpClientFactory _httpClientFactory;
public MainWindow(IHttpClientFactory httpClientFactory)
{
InitializeComponent();
_httpClientFactory = httpClientFactory;
}
private async void ButtonExit_Click(object sender, RoutedEventArgs e)
{
var client = _httpClientFactory.CreateClient();
var html = await client.GetStringAsync("http://www.baidu.com");
//Application.Current.Shutdown();
}
}


这就是关于 HttpClientFactory 的简单使用。


使用 EFCore


最后介绍的一大利器就是巨硬的 EFCore,这个东西也很溜,值得我们尝试使用。这里我使用 Sqlite 为例。


我们新建一个基于 DotNetCore 3.0 的 WPF 项目,然后引入如下包:


  • Microsoft.Extensions.DependencyInjection


  • Microsoft.Extensions.Configuration.FileExtensions


  • Microsoft.Extensions.Configuration.Json


  • Microsoft.EntityFrameworkCore.Sqlite


首先,我们参考上面提到的使用方式,在项目根目录下创建一个 appsettings.json,文件,修改内容如下所示:


{
"ConnectionStrings": {
"SqlConnection": "datasource = default.sqlite"
}
}


然后将该文件的 Build Action 设置为 Content, To Output Directiory 设置为  if newer。


接着,我们创建一个 DataContext 类,示例代码如下所示:


public class DataContext : DbContext
{
public DataContext(DbContextOptions options) : base(options)
{
this.Database.Migrate();
}
}


然后删除掉 App.xaml 中的 StartupUri="MainWindow.xaml",并修改 App.xaml.cs,示例代码如下所示:


public partial class App : Application
{
public ServiceProvider ServiceProvider { get; private set; }
public IConfigurationRoot 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();
ConfigurationServices(serviceCollection);
ServiceProvider = serviceCollection.BuildServiceProvider();
var mainView = ServiceProvider.GetRequiredService<MainWindow>();
mainView.Show();
base.OnStartup(e);
}
private void ConfigurationServices(ServiceCollection services)
{
services.AddTransient(typeof(MainWindow));
services.AddDbContext<DataContext>(options=>options.UseSqlite(Configuration.GetConnectionString("SqlConnection")));
}
}


然后我们修改 MainWindow.xaml.cs,示例代码如下所示:


public partial class MainWindow : Window
{
private readonly DataContext _dataContext;
public MainWindow(DataContext dataContext)
{
InitializeComponent();
_dataContext = dataContext;
}
private void ButtonExit_Click(object sender, RoutedEventArgs e)
{
Application.Current.Shutdown();
}
}


使用方法依然很简单。


支持 UWP 相关控件 和 Windows10 API


传统的 WPF 客户端,如果使用基于 UWP 的相关控件,则可以通过使用 WindowsCommunityToolkit 控件库来使用 UWP 的相关控件,该控件库目前可能还不是很完善,但是微软已经在不断添加新功能了。


UWP 是未来发展的趋势,但是对于传统的 WPF,如果想像 UWP 那样也能使用功能更加强大的 API,只需要通过简单添加一些引用就可以实现。微软之前有发布过具体使用的文章,文末有给出链接。


发布方式


基于 DotNetCore 3.0 的 WPF 项目发布方式还是和传统的 WPF 项目发布方式有所差异。全新的发布方式是基于 DotNetCore 的风格来进行设计的。在 Publish 的选项卡中,我们可以看到如下配置



我们可以依据具体情况,来选择合适的发布方式进行发布。当然,我们也可以借助 Desktop App Converter 工具,将我们的应用分发到 windows Store 上。


总结


通过上述几个简单的示例,我们可以看到传统的 WPF 已经被微软注入了新鲜的血液,并且在微软生态下的 C/S端 和 B/S端 开发模式渐趋相同。大大减轻了学习曲线。能够让技术在最短的时间里变现,这也是我最看重的地方。


无论是过去还是现在,我都时不时地听身边的人说不应该过度依赖工具,但是我想说的是 技术服务于现实,而工具只是为了加速变现,如果一项技术再好再优秀,它却不能创造现实价值,服务生活,那么我宁愿放弃使用它。微软为开发者提供了 DotNetCore 的好技术,而 VisualStudio 系列工具作为生产力工具,只是为了提高生产效率。在效率为王的今天,谁赢得了时间,就赢得了一切。


最后,我不打算吹捧 DotNetCore、WPF、VisualStudio,以免有人说我会误导萌新。还是那句话,实践出真知,感兴趣的话可以自己动手尝试一下。


此外,目前 DotNetCore 3.0 还是处于预览阶段,所以可能会有一些坑。但是谁能保证自己第一次就能把事情做的完美呢?时间会证明一切。


补充


评论中很多朋友问到跨平台的问题,所以我补充了一张图。



推荐阅读

(点击标题可跳转阅读)

实际体验Span<T> 的惊人表现

C#8.0可空引用类型的使用注意要点

.NET Core新型ORM功能介绍


看完本文有收获?请转发分享给更多人

关注「DotNet」加星标,提升.Net技能 

喜欢就点一下「在看」呗~

登录查看更多
1

相关内容

【2020新书】使用高级C# 提升你的编程技能,412页pdf
专知会员服务
57+阅读 · 2020年6月26日
FPGA加速系统开发工具设计:综述与实践
专知会员服务
65+阅读 · 2020年6月24日
【实用书】Python爬虫Web抓取数据,第二版,306页pdf
专知会员服务
117+阅读 · 2020年5月10日
深度神经网络实时物联网图像处理,241页pdf
专知会员服务
76+阅读 · 2020年3月15日
【新书】Java企业微服务,Enterprise Java Microservices,272页pdf
【干货】大数据入门指南:Hadoop、Hive、Spark、 Storm等
专知会员服务
95+阅读 · 2019年12月4日
微信小程序支持webP的WebAssembly方案
前端之巅
19+阅读 · 2019年8月14日
用Now轻松部署无服务器Node应用程序
前端之巅
16+阅读 · 2019年6月19日
Pupy – 全平台远程控制工具
黑白之道
43+阅读 · 2019年4月26日
使用 C# 和 Blazor 进行全栈开发
DotNet
6+阅读 · 2019年4月15日
C# 10分钟完成百度人脸识别
DotNet
3+阅读 · 2019年2月17日
占坑!利用 JenKins 持续集成 iOS 项目时遇到的问题
.NET Core 环境下构建强大且易用的规则引擎
深度学习 | 免费使用Google Colab的GPU云计算平台
沈浩老师
12+阅读 · 2018年2月4日
微软发布Visual Studio Tools for AI
AI前线
4+阅读 · 2017年11月20日
Directions for Explainable Knowledge-Enabled Systems
Arxiv
26+阅读 · 2020年3月17日
Arxiv
35+阅读 · 2019年11月7日
Arxiv
9+阅读 · 2019年4月19日
Learning Blind Video Temporal Consistency
Arxiv
3+阅读 · 2018年8月1日
Arxiv
6+阅读 · 2018年1月11日
VIP会员
相关资讯
微信小程序支持webP的WebAssembly方案
前端之巅
19+阅读 · 2019年8月14日
用Now轻松部署无服务器Node应用程序
前端之巅
16+阅读 · 2019年6月19日
Pupy – 全平台远程控制工具
黑白之道
43+阅读 · 2019年4月26日
使用 C# 和 Blazor 进行全栈开发
DotNet
6+阅读 · 2019年4月15日
C# 10分钟完成百度人脸识别
DotNet
3+阅读 · 2019年2月17日
占坑!利用 JenKins 持续集成 iOS 项目时遇到的问题
.NET Core 环境下构建强大且易用的规则引擎
深度学习 | 免费使用Google Colab的GPU云计算平台
沈浩老师
12+阅读 · 2018年2月4日
微软发布Visual Studio Tools for AI
AI前线
4+阅读 · 2017年11月20日
Top
微信扫码咨询专知VIP会员