DotNetCore 3.0 助力 WPF 开发

DotNetCore Is AnyWhere.

前言

Visual Studio 2019 已经正式发布了,DotNetCore 3.0 的正式版也指日可待。在之前的版本中,作为一名基于微软生态的传统 WPF 程序员看着隔壁同学在开发 DotNetCore 网站时用着各种特性好生羡慕,想着巨硬啥时候能让客户端开发者也能尝尝甜头。

那么,现在是时候可以尝试一下了。

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

开发环境准备

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

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

直接默认安装即可。

全新的开发体验

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

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

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

Hello World

首先,我们可以通过 VS 创建一个基于 DotNetCore 的 项目模板,然后我们看一下与传统的 WPF 项目模板有什么区别。如下图所示,创建一个 WPF 项目

创建完成后,尝试编译编译运行(注:第一次编译可能需要较长时间),如下图所示

如果我们仔细看一下这个新版的项目模板,会发现与传统的项目模板相比,有好几处发生了改变:

  • **.csproj 的组织方式发生了改变,与传统的组织方式相比,内容精简的快没有了;
  • 项目默认会引用 Microsoft.NETCore.PlatformsMicrosoft.WindowsDesktop.App,这两个 Package 都是针对 WinFormWPF 的特定包
  • 项目属性中也有一些改动
  • 生成目录中也有改动,会生成一些以 json 结尾的文件

上述这些改动都是最直观的改动,但是这些改动貌似不痛不痒,并不能吸引传统 WPF 开发者投入使用。接触过 DotNetCore Web 方向的开发者已经对里面的 DI,HttpClientFactory,EFCore 等使用的炉火纯青,那我们能不能在 WPF 中也使用这些东西呢?答案是必须要能啊,所有我们还需要探索一下它的一些硬核功能。下面列举几个我目前知道的几个我觉得很炫酷的功能。

使用 DI 和 Service Provider

能够使用 DIService 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 设置为 ContentCopy 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(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** 的值,如下图所示:

通过这个简单的例子,我们可以看到这种全新方式的依赖注入已经得到微软的大力支持,将基于 .NetCoreCS模式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 设置为 ContentCopy To Output Directiory 设置为 Copy 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 系列工具作为生产力工具,只是为了提高生产效率。在效率为王的今天,谁赢得了时间,就赢得了一切。

最后,我不打算吹捧 DotNetCoreWPFVisualStudio,以免有人说我会误导萌新。还是那句话,实践出真知,感兴趣的话可以自己动手尝试一下。此外,目前 DotNetCore 3.0 还是处于预览阶段,所以可能会有一些坑。但是谁能保证自己第一次就能把事情做的完美呢?时间会证明一切。

相关参考

原文地址:https://www.cnblogs.com/hippieZhou/p/10637348.html

时间: 2024-11-08 23:04:40

DotNetCore 3.0 助力 WPF 开发的相关文章

DotNetCore 3.0 助力 WPF本地化

概览 随着我们的应用程序越来越受欢迎,我们的下一步将要开发多语言功能.方便越来越多的国家使用我们中国的应用程序, 基于 WPF 本地化,我们很多时候使用的是系统资源文件,可是动态切换本地化,就比较麻烦了. 有没有一种方法既可以适用系统的资源文件,又能方便快捷的切换本地化呢? 实现思路 现在我们将要实现的是基于 DotNetCore 3.0 以上版本 and WPF 桌面应用程序模块化的多语言功能. 动态切换多语言思路: 把所有模块的资源文件添加到字典集合. 将资源文件里的key,绑定到前台. 通

使用 MSIX 打包 DotNetCore 3.0 客户端程序

原文:使用 MSIX 打包 DotNetCore 3.0 客户端程序 如何你希望你的 WPF 程序能够以 Windows 的保护机制保护起来,不被轻易反编译的话,那么这篇文章应该能帮到你. 介绍 MSIX 是微软于去年的 Windows 开发者日峰会 上推出的全新应用打包解决方案.其目的是取代旧式的软件打包方式,可用于 Win32.WindowsForm . WPF 和 UWP 等应用程序,该打包方式将支持 Windows7 和 Windows8.x.并且让我们的程序不会轻易反编译. 本文,我们

在 DotNetCore 3.0 程序中使用通用协议方式启动文件关联应用

原文:在 DotNetCore 3.0 程序中使用通用协议方式启动文件关联应用 问题描述 在传统的基于 .NET Framework 的 WPF 程序中,我们可以使用如下代码段启动相关的默认应用: # 启动默认文本编辑器打开 helloworld.txt Process.Start("helloworld.txt"); # 启动默认浏览器打开 https://hippiezhou.fun/ Process.Start("https://hippiezhou.fun/"

DotNetCore.1.0.1-VS2015Tools.Preview2.0.3 相关问题及解决办法

本月16号,MS发布了 .NET Core 1.1.作为一个用贯MS产品的小盆友,我第一时间就把相关的安装包下载下来了,然后果断安装(入坑). 我猜你来看这篇博客可能遇到了和我一样的问题. 问题0:正确的安装顺需 正确的顺序在MS的dotnet Core官网上可以找到,请根据自己的VS版本对号入座. 如果你觉得这个太长或者存在疑问,简短的版本是: 1.VS2015 1. 检查VS2015 是否已经安装了Update3.3:打开VS2015,然后点击[帮助]-[关于Microsoft Visual

拥抱新的.Net开发框架,WPF开发人员怎样向.Net迁移

ArcGIS Runtime 10.2版本号中.将WindowsPhone .WindowsStore以及WPF三大SDK整合成了一个全新的SDK--ArcGISRuntime SDK for Microsoft .Net Framework,简称.Net SDK.同一时候现有的WPF SDK能够继续使用.但兴许会停止更新.因此,Esri建议WPF开发人员们向.Net阵营迁移. 顾名思义,新的.NetSDK面向微软的.Net框架,曾经的WPF.Windows Phone以及Windows Sto

wpf开发桌面软件记录

我的开发环境是win7,vs2013,sql2012,用wpf开发了一个很简单的桌面软件,用Installshield制作的安装包,安装包包含了.framework4.5,在自己电脑上测试正常,想着挺简单的啊 后来拿了一台xp系统的电脑,安装了测试,安装过程正常,可是打开软件的时候报错:不是有效的win32 第一个想法就是 版本不合,要使用兼容打开,可是,我发现xp根本就没有兼容打开软件的操作 第二个想法是vs里面是不是可以设置支持32位,可是重复看了vs,发现项目属性里面,支持系统位数 首选3

WPF 开发自动开机启动程序

原文:WPF 开发自动开机启动程序 本文告诉大家如何在 WPF 开发一个可以自动启动的程序 本文使用的自动开机启动方法是通过快捷方式放在启动文件夹的方式. 创建快捷方式 /// <summary> /// 为本程序创建一个快捷方式. /// </summary> /// <param name="lnkFilePath">快捷方式的完全限定路径.</param> /// <param name="args">

华为云EI ModelArts,从0到1开发训练AI模型,通过“极快”和“极简”实现普惠AI

华为云EI ModelArts,从0到1开发训练AI模型,通过"极快"和"极简"实现普惠AI现如今 AI 技术.概念火爆.落地应用更是繁多,但开发呢?是否困难?到底有多痛?据了解,大部分 AI 开发者的工作时间并不长,并且十有八九可能不是"科班出身".从编写的教材.录制的课程中就可以看出,所有的教学都不可避免地带有很强的的学术性,即便有意避免研究导向,仍然离产业界的需求相去甚远.并且随着新一波人工智能的热潮,人们发现手里的数据多了,电脑运算的更快

采用WPF开发截图程序,so easy!

原文:采用WPF开发截图程序,so easy! 前言  QQ.微信截图功能已很强大了,似乎没必要在开发一个截图程序了.但是有时QQ热键就是被占用,不能快速的开启截屏:有时,天天挂着QQ,领导也不乐意.既然是程序员,就要自己开发截屏工具,功能随心所欲,岂不快哉. 再强调一点:工具就是生产力!没有掌握WPF之前,我是不会开发这么一个程序的,如果采用MFC.winform框架,工作量是相当的大,开发出来的效果肯定也比较low.本人用WPF,花了一天的功夫,开发了这个小程序.程序的定位就功能简单,平时工