DotNetCore 3.0 助力 WPF 开发
前言
Visual Studio 2019?已經(jīng)正式發(fā)布了,DotNetCore 3.0?的正式版也指日可待。在之前的版本中,作為一名基于微軟生態(tài)的傳統(tǒng)?WPF?程序員看著隔壁同學(xué)在開發(fā) DotNetCore 網(wǎng)站時(shí)用著各種特性好生羨慕,想著巨硬啥時(shí)候能讓客戶端開發(fā)者也能嘗嘗甜頭。
那么,現(xiàn)在是時(shí)候可以嘗試一下了。
需要說(shuō)明的一點(diǎn)的是,DotNetCore 3.0?雖然跨平臺(tái),但是基于此的 WPF 卻是針對(duì) Windows 特定平臺(tái)的實(shí)現(xiàn),并不能跨 Linux 和 MacOS 。
開發(fā)環(huán)境準(zhǔn)備
要想開發(fā)?DotNetCore?版本的 WPF,首先需要確保我們的機(jī)器上已經(jīng)安裝了如下
Visual Studio 2019?下載地址
需要安裝的組件如下圖所示
DotNetCore 3.0 SDK?下載地址
直接默認(rèn)安裝即可。
全新的開發(fā)體驗(yàn)
在首次使用 VS2019 創(chuàng)建 DotNetCore 版本的 WPF 程序時(shí),VS 可能會(huì)給你爆個(gè)如下圖所示的錯(cuò)誤:
按照錯(cuò)誤提示即可解決該問(wèn)題,如下圖所示
接著選擇?TOOLS?->?Options,配置如下圖所示
Hello World
首先,我們可以通過(guò) VS 創(chuàng)建一個(gè)基于 DotNetCore 的 項(xiàng)目模板,然后我們看一下與傳統(tǒng)的 WPF 項(xiàng)目模板有什么區(qū)別。如下圖所示,創(chuàng)建一個(gè) WPF 項(xiàng)目
創(chuàng)建完成后,嘗試編譯編譯運(yùn)行(注:第一次編譯可能需要較長(zhǎng)時(shí)間),如下圖所示
如果我們仔細(xì)看一下這個(gè)新版的項(xiàng)目模板,會(huì)發(fā)現(xiàn)與傳統(tǒng)的項(xiàng)目模板相比,有好幾處發(fā)生了改變:
**.csproj 的組織方式發(fā)生了改變,與傳統(tǒng)的組織方式相比,內(nèi)容精簡(jiǎn)的快沒(méi)有了;
項(xiàng)目默認(rèn)會(huì)引用?Microsoft.NETCore.Platforms?和?Microsoft.WindowsDesktop.App,這兩個(gè) Package 都是針對(duì)?WinForm?和?WPF?的特定包
項(xiàng)目屬性中也有一些改動(dòng)
生成目錄中也有改動(dòng),會(huì)生成一些以?json?結(jié)尾的文件
上述這些改動(dòng)都是最直觀的改動(dòng),但是這些改動(dòng)貌似不痛不癢,并不能吸引傳統(tǒng) WPF 開發(fā)者投入使用。接觸過(guò)?DotNetCore Web?方向的開發(fā)者已經(jīng)對(duì)里面的 DI,HttpClientFactory,EFCore 等使用的爐火純青,那我們能不能在 WPF 中也使用這些東西呢?答案是必須要能啊,所有我們還需要探索一下它的一些硬核功能。下面列舉幾個(gè)我目前知道的幾個(gè)我覺(jué)得很炫酷的功能。
使用 DI 和 Service Provider
能夠使用?DI?和?Service Provider,這是我覺(jué)得最值得說(shuō)一下的,因?yàn)樗氖褂梅绞胶?jiǎn)直和在?DotNetCore Web?里面的一摸一樣,值得一說(shuō)。
首先,我們創(chuàng)建一個(gè) DotNetCore 版本的 WPF 項(xiàng)目,然后引用如下包:
Microsoft.Extensions.DependencyInjection
Microsoft.Extensions.Options.ConfigurationExtensions
Microsoft.Extensions.Configuration.FileExtensions
Microsoft.Extensions.Configuration.Json
注:上述包都需要安裝 預(yù)覽版本的 3.0.0 版本的才行
然后,在我們的項(xiàng)目根目錄下創(chuàng)建一個(gè)?appsettings.json?文件,文件內(nèi)容如下所示:
{"AppSettings": {
"StringSetting": "Value",
"IntegerSetting": 42,
"BooleanSetting": true
}
}
然后將該文件的?Build Action?設(shè)置為?Content,Copy To Output Directiory?設(shè)置為?Copy if newer。
接著,我們?cè)陧?xiàng)目根目錄下創(chuàng)建一個(gè)?AppSettings.cs?文件,用于隱射上面的?appsettings.json?文件,示例代碼如下所示:
public class AppSettings{
public string StringSetting { get; set; }
public int IntegerSetting { get; set; }
public bool BooleanSetting { get; set; }
}
然后,我們創(chuàng)建一個(gè)自定義的接口服務(wù)?ISampleService和對(duì)應(yīng)實(shí)現(xiàn)?SampleService,示例代碼如下所示:
public interface ISampleService{
string GetCurrentDate();
}
public class SampleService : ISampleService
{
public string GetCurrentDate() => DateTime.Now.ToLongDateString();
}
然后,修改我們的?App.xaml?文件,刪除掉默認(rèn)添加的啟動(dòng)視圖代碼?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);
// 獲取配置建造器創(chuàng)建的對(duì)象
Configuration = builder.Build();
//配置全局服務(wù)容器
var serviceCollection = new ServiceCollection();
ConfigureServices(serviceCollection);
ServiceProvider = serviceCollection.BuildServiceProvider();
var mainWindow = ServiceProvider.GetRequiredService<MainWindow>();
mainWindow.Show();
}
private void ConfigureServices(IServiceCollection services)
{
// 向全局容器中注冊(cè)一個(gè)視圖
services.AddTransient(typeof(MainWindow));
// 在全局容器中配置 AppSettings
services.Configure<AppSettings>(Configuration.GetSection(nameof(AppSettings)));
// 在全局容器中注冊(cè)自定義服務(wù)
services.AddScoped<ISampleService, SampleService>();
}
}
修改完畢后,我們可以嘗試編譯我們的項(xiàng)目,如果不出意外的啊,我們的程序會(huì)正常啟動(dòng)起來(lái)。這里就不做截圖說(shuō)明了。
看了上述代碼,是不是覺(jué)得很有意思啊,這種全新的開發(fā)模式頓時(shí)把我們的代碼水平提升了好幾個(gè)檔次。這種使用方式和在?AspDotNetCore?簡(jiǎn)直一摸一樣。比如,我們?cè)谏厦孀?cè)了?AppSettings?和一個(gè)基于?ISampleService?接口的實(shí)現(xiàn)?SampleService,那么我們就可以在?MainWindow?的構(gòu)造函數(shù)中使用,比如,我們可以參考下面的示例代碼:
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();
}
}
然后,我們可以監(jiān)視一下 **_settings** 的值,如下圖所示:
通過(guò)這個(gè)簡(jiǎn)單的例子,我們可以看到這種全新方式的依賴注入已經(jīng)得到微軟的大力支持,將基于?.NetCore?的?CS模式?和?BS模式?開發(fā)方式進(jìn)行了統(tǒng)一,學(xué)習(xí)曲線是不是又下降了很多啊。
使用 HttpClientFactory
眾所周知,HttpClient 在實(shí)際的使用場(chǎng)景中還是存在一些弊端,在?DotNetCore?的 Web 端中,很多同學(xué)用了?HttpClientFactory?如魚得水,減少了很多不必要的麻煩。現(xiàn)在,我們同樣可以將這一利器在 WPF 中使用。
我們新建一個(gè)基于?DotNetCore 3.0?的 WPF 項(xiàng)目,然后引入如下包:
Microsoft.Extensions.DependencyInjection
Microsoft.Extensions.Http
然后,修改我們的?App.xaml?文件,刪除掉默認(rèn)添加的啟動(dòng)視圖代碼?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();
}
}
這就是關(guān)于?HttpClientFactory?的簡(jiǎn)單使用。
使用 EFCore
最后介紹的一大利器就是巨硬的?EFCore,這個(gè)東西也很溜,值得我們嘗試使用。這里我使用?Sqlite?為例。
我們新建一個(gè)基于?DotNetCore 3.0?的 WPF 項(xiàng)目,然后引入如下包:
Microsoft.Extensions.DependencyInjection
Microsoft.Extensions.Configuration.FileExtensions
Microsoft.Extensions.Configuration.Json
Microsoft.EntityFrameworkCore.Sqlite
首先,我們參考上面提到的使用方式,在項(xiàng)目根目錄下創(chuàng)建一個(gè)?appsettings.json,文件,修改內(nèi)容如下所示:
{"ConnectionStrings": {
"SqlConnection": "datasource = default.sqlite"
}
}
然后將該文件的?Build Action?設(shè)置為?Content,Copy To Output Directiory?設(shè)置為?Copy if newer。
接著,我們創(chuàng)建一個(gè)?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();
}
}
使用方法依然很簡(jiǎn)單。
支持 UWP 相關(guān)控件 和 Windows10 API
傳統(tǒng)的 WPF 客戶端,如果使用基于 UWP 的相關(guān)控件,則可以通過(guò)使用?WindowsCommunityToolkit控件庫(kù)來(lái)使用 UWP 的相關(guān)控件,該控件庫(kù)目前可能還不是很完善,但是微軟已經(jīng)在不斷添加新功能了。
UWP?是未來(lái)發(fā)展的趨勢(shì),但是對(duì)于傳統(tǒng)的 WPF,如果想像 UWP 那樣也能使用功能更加強(qiáng)大的 API,只需要通過(guò)簡(jiǎn)單添加一些引用就可以實(shí)現(xiàn)。微軟之前有發(fā)布過(guò)具體使用的文章,文末有給出鏈接。
發(fā)布方式
基于?DotNetCore 3.0?的 WPF 項(xiàng)目發(fā)布方式還是和傳統(tǒng)的 WPF 項(xiàng)目發(fā)布方式有所差異。全新的發(fā)布方式是基于?DotNetCore?的風(fēng)格來(lái)進(jìn)行設(shè)計(jì)的。在?Publish?的選項(xiàng)卡中,我們可以看到如下配置
我們可以依據(jù)具體情況,來(lái)選擇合適的發(fā)布方式進(jìn)行發(fā)布。當(dāng)然,我們也可以借助?Desktop App Converter?工具,將我們的應(yīng)用分發(fā)到 windows Store 上。
總結(jié)
通過(guò)上述幾個(gè)簡(jiǎn)單的示例,我們可以看到傳統(tǒng)的 WPF 已經(jīng)被微軟注入了新鮮的血液,并且在微軟生態(tài)下的?C/S端?和?B/S端?開發(fā)模式漸趨相同。大大減輕了學(xué)習(xí)曲線。能夠讓技術(shù)在最短的時(shí)間里變現(xiàn),這也是我最看重的地方。
無(wú)論是過(guò)去還是現(xiàn)在,我都時(shí)不時(shí)地聽身邊的人說(shuō)不應(yīng)該過(guò)度依賴工具,但是我想說(shuō)的是?技術(shù)服務(wù)于現(xiàn)實(shí),而工具只是為了加速變現(xiàn),如果一項(xiàng)技術(shù)再好再優(yōu)秀,它卻不能創(chuàng)造現(xiàn)實(shí)價(jià)值,服務(wù)生活,那么我寧愿放棄使用它。微軟為開發(fā)者提供了?DotNetCore?的好技術(shù),而?VisualStudio?系列工具作為生產(chǎn)力工具,只是為了提高生產(chǎn)效率。在效率為王的今天,誰(shuí)贏得了時(shí)間,就贏得了一切。
最后,我不打算吹捧?DotNetCore、WPF、VisualStudio,以免有人說(shuō)我會(huì)誤導(dǎo)萌新。還是那句話,實(shí)踐出真知,感興趣的話可以自己動(dòng)手嘗試一下。此外,目前?DotNetCore 3.0?還是處于預(yù)覽階段,所以可能會(huì)有一些坑。但是誰(shuí)能保證自己第一次就能把事情做的完美呢?時(shí)間會(huì)證明一切。
補(bǔ)充
評(píng)論中很多朋友問(wèn)到跨平臺(tái)的問(wèn)題,所以我補(bǔ)充了一張圖。
相關(guān)參考
What's new in .NET Core 3.0 (Preview 2)
Around and About .NET World
Calling Windows 10 APIs From a Desktop Application
原文地址:https://www.cnblogs.com/hippieZhou/p/10637348.html
.NET社區(qū)新聞,深度好文,歡迎訪問(wèn)公眾號(hào)文章匯總 http://www.csharpkit.com
總結(jié)
以上是生活随笔為你收集整理的DotNetCore 3.0 助力 WPF 开发的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: ASP.NET Core Web 项目文
- 下一篇: 《从零开始学ASP.NET CORE M