使用 DotNet CLI 創建自定義的 DotNetCore 3.0 WPF 項目模板


描述

當我們安裝完 DotNetCore 3.0 版本的 SDK 后,我們就可以創建基於 DotNetCore 的 WPF 項目模板,通過如下 CLI 可以方便快捷的創建並運行我們的項目:

dotnet new wpf -n WpfApp
cd WpfApp
dotnet restore
dotnet run

做過 WPF 開發的朋友都知道,這個項目模板肯定不符合我們的預期,我們希望我們的項目模板能夠加入 MVVM 的默認代碼段,並且能夠和 DotNetCore 緊密合作,這樣豈不是更加方便了嗎? 所以本文使用 MVVM 的一種實現 MvvmLightStd10 來教大家如何創建一個我們理想的項目模板。

操作

首先,我們基於 DotNetCore 3.0 創建一個原始的 WPF 項目模板,然后引用如下庫:

  • Microsoft.Extensions.DependencyInjection
  • MvvmLightLibsStd10

可通過執行 cli 命令進行安裝

dotnet add package Microsoft.Extensions.DependencyInjection
dotnet add package MvvmLightLibsStd10

注:因為我們使用了 DotNetCore,所以我們盡量讓我們安裝的第三方包是基於 .NET Standard 方式來實現。

然后,嘗試修改我們的這個項目,把它改成我們以后期望創建的項目模板的樣子。可以參考我的如下修改:

項目結構如下圖所示:

其中,src\Models\DataItem.cs 的示例代碼如下所示:

using System;
using System.Collections.Generic;
using System.Text;

namespace WpfApp.Models
{
    public class DataItem
    {
        public string Title { get; private set; }

        public DataItem(string title)
        {
            Title = title;
        }
    }
}

src\Models\IDataService.cs 的示例代碼如下所示:

using System;
using System.Collections.Generic;
using System.Text;

namespace WpfApp.Models
{
    public interface IDataService
    {
        void GetData(Action<DataItem, Exception> callback);
    }
}

src\Models\DataService.cs 的示例代碼如下所示:

using System;
using System.Collections.Generic;
using System.Text;

namespace WpfApp.Models
{
    public class DataService : IDataService
    {
        public void GetData(Action<DataItem, Exception> callback)
        {
            var item = new DataItem("Hello .NET Core!");
            callback(item, null);
        }
    }
}

src\ViewModels\MainViewModel.cs 的示例代碼如下所示:

using GalaSoft.MvvmLight;
using WpfApp.Models;

namespace WpfApp.ViewModels
{
    public class MainViewModel : ViewModelBase
    {
        private readonly IDataService _dataService;

        private string _welcomeTitle;
        public string WelcomeTitle
        {
            get { return _welcomeTitle; }
            set { Set(ref _welcomeTitle, value); }
        }

        public MainViewModel(IDataService dataService)
        {
            _dataService = dataService;
              _dataService.GetData(
                (item, error) =>
                {
                    if (error != null)
                    {
                        return;
                    }

                    WelcomeTitle = item.Title;
                });
        }
    }
}

src\Views\MainView.xaml 的示例代碼如下所示:

<Window
    x:Class="WpfApp.Views.MainView"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:local="clr-namespace:WpfApp"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    Title="MainWindow"
    Width="800"
    Height="450"
    mc:Ignorable="d">
    <Grid>
        <Label
            HorizontalAlignment="Center"
            VerticalAlignment="Center"
            Content="{Binding WelcomeTitle}"
            FontSize="40" />
    </Grid>
</Window>

src\Views\MainView.xaml.cs 的示例代碼如下所示:

using System.Windows;
using WpfApp.ViewModels;

namespace WpfApp.Views
{
    public partial class MainView : Window
    {
        public MainView(MainViewModel vm)
        {
            InitializeComponent();
            this.DataContext = vm;
        }
    }
}

src\App.xaml 的示例代碼如下所示:

<Application
    x:Class="WpfApp.App"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:WpfApp" />

src\App.xaml.cs 的示例代碼如下所示:

using Microsoft.Extensions.DependencyInjection;
using System.Windows;
using WpfApp.Models;
using WpfApp.ViewModels;
using WpfApp.Views;

namespace WpfApp
{
    public partial class App : Application
    {
        public ServiceProvider ServiceProvider { get; private set; }

        protected override void OnStartup(StartupEventArgs e)
        {
            base.OnStartup(e);

            var serviceCollection = new ServiceCollection();
            ConfigureServices(serviceCollection);

            ServiceProvider = serviceCollection.BuildServiceProvider();

            var mainWindow = ServiceProvider.GetRequiredService<MainView>();
            mainWindow.Show();
        }

        private void ConfigureServices(ServiceCollection services)
        {
            services.AddTransient<MainView>();
            services.AddTransient<MainViewModel>();

            services.AddScoped<IDataService, DataService>();
        }
    }
}

修改完畢后嘗試編譯運行我們的項目,確保可以正常編譯運行。

之后,在我們的項目根目錄 src 下新建一個 .template.config 文件夾,然后在里面新建一個 template.json 文件,進行如下示例配置:

{
    "$schema": "http://json.schemastore.org/template",
    "author": "hippiezhou <hippiezhou@outlook.com>",
    "classifications": ["wpf", "mvvmlight", "Dependency Injection"],
    "name": "wpf mvvmlight: use dotnetcore to create wpf with mvvmlight.",
    "tags": {
        "language": "C#",
        "type": "project"
    },
    "identity": "wpf.mvvmlight",
    "shortName": "wpf-mvvmlight",
    "sourceName": "wpf.mvvmlight",
    "preferNameDirectory": true
}

最后,打開我們的終端,將目錄切換至當前項目目錄下(就是 .template.config 所在的目錄),然后執行下述安裝操作

dotnet new -i C:\Users\hippieZhou\Desktop\helloworld\wpfapp

此時,我們的項目模板會被打包到 DotNetCore 的 CLI 中,如下圖所示:

同時,在 C:\Users\hippieZhou.templateengine\dotnetcli\v3.0.100-preview3-010431 目錄下的以 templatecache.json 結尾的 JSON 文件內容也會發生修改,會在 TemplateInfo 結點下新增一個如下的節點內容:

 {
      "ConfigMountPointId": "f3861181-7a43-4fc5-ab1c-12d95e734c0a",
      "Author": "hippiezhou <hippiezhou@outlook.com>",
      "Classifications": [
        "wpf",
        "mvvmlight",
        "Dependency Injection"
      ],
      "DefaultName": null,
      "Description": "",
      "Identity": "wpf.mvvmlight",
      "GeneratorId": "0c434df7-e2cb-4dee-b216-d7c58c8eb4b3",
      "GroupIdentity": "",
      "Precedence": 0,
      "Name": "wpf mvvmlight: use dotnetcore to create wpf with mvvmlight.",
      "ShortNameList": [
        "wpf-mvvmlight"
      ],
      "Tags": {
        "language": {
          "Description": null,
          "ChoicesAndDescriptions": {
            "C#": ""
          },
          "DefaultValue": "C#"
        },
        "type": {
          "Description": null,
          "ChoicesAndDescriptions": {
            "project": ""
          },
          "DefaultValue": "project"
        }
      },
      "CacheParameters": {
        "name": {
          "DataType": "string",
          "DefaultValue": null,
          "Description": "The default name symbol"
        }
      },
      "ConfigPlace": "/.template.config/template.json",
      "LocaleConfigMountPointId": "00000000-0000-0000-0000-000000000000",
      "LocaleConfigPlace": null,
      "HostConfigMountPointId": "00000000-0000-0000-0000-000000000000",
      "HostConfigPlace": null,
      "ThirdPartyNotices": null,
      "BaselineInfo": {},
      "HasScriptRunningPostActions": false,
      "ConfigTimestampUtc": null
},

注:如果后期我們不慎將我們的模板刪除了,我們通過刪除掉這兩個文件里面對應的模板節點就可以在 CLI 中取消應用了。

我們可以使用下述操作進行測試一下:

# 使用我們自定義的項目模板,創建 wpf 項目
dotnet new wpf-mvvmlight -n test

cd test

dotnet restore

dotnet run

如果不出意外的話,我們就可以看到這個項目的代碼段和我們自定義的模板代碼段是一樣的。

如果卸載我們的項目模板可以使用如下命令:

dotnet new -u C:\Users\hippieZhou\Desktop\helloworld\wpfapp

注:我們需要確保我們的自定義模板不能丟失,要不然到時候就卸載就麻煩了(至少目前看來是這樣的)。

關於如何將我們的自定義模板可以上傳到 NuGet 供別人下載使用,這里就不做介紹了,具體操作可參考園里介紹如何在 DotNetCore MVC 中打造自己的項目模板方法是一樣的。我在本文中的創建的代碼模板也不會提交上去,還是等着 MVVMLight 的原作者 Laurent Bugnion 來操刀會好一些。

總結

本文介紹了如何通過 DotNet CLI 來創建自定義的 WPF 項目模板。在實際的使用過程中,CLI 的功能和支持的參數會更多,所以感興趣的朋友可以自行研究。

相關參考


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM