1. 升級到Core的好處 去年中我曾考慮將我的控制項庫項目 "Kino.Toolkit.Wpf" 升級到.NET Core,不過很快放棄了,因為當時.NET Core是預覽版,編譯WPF還需要使用最新的Visual Studio 2019,這樣作為一個教學項目不夠友好。到了今天.NET Core 3 ...
1. 升級到Core的好處
去年中我曾考慮將我的控制項庫項目Kino.Toolkit.Wpf升級到.NET Core,不過很快放棄了,因為當時.NET Core是預覽版,編譯WPF還需要使用最新的Visual Studio 2019,這樣作為一個教學項目不夠友好。到了今天.NET Core 3.1都出來了,已經正式支持WPF和Winform,Visual Studio 2019也已經普及,我覺得應該是時候將我的控制項庫升級到.NET Core。那麼現在是WPF正式遷移到.NET Core的好時機嗎?我認為還不是,把一個成熟的WPF程式遷移到.NET Core風險任然較大,而且不見得有多少好處。但對各種WPF類庫/控制項庫來說情況又不一樣了,為了可以滿足更多的用戶,讓控制項庫可以同時支持.NET Framework和.NET Core十分重要;而且通常類庫對其它組件的依賴較少,升級的風險沒那麼大。所以要玩.NET Core的WPF,從類庫/控制項庫開始是一個好的選擇。
具體來說,讓WPF控制項庫升級到.NET Core具體來說有以下的好處:
- 巨大的時髦值,最近WPF開發時髦值很低,.NET Core是我們為數不多可以蹭到時髦值、面向時髦值編程的機會。
- 新的csproj文件,順便升級到新的SDK-style csproj文件有很多好處,包括更簡潔可讀的文件,新的NuGet引用方式,可以指定多個開發框架等。
- 更方便打包Nuget。
升級到.NET Core 3.1有以下步驟:
- 分析可移植性
- 遷移到
NuGet 引用 - 遷移csproj項目文件
這篇文章我會以我的Kino.Toolkit.Wpf項目作為示例,master分支不升級,而core升級到core 3.1以作比較。需要註意的是,WPF控制項庫的升級和其它.NET項目的升級有一點出入,這篇文章的升級方式不一定適合其它.NET Core項目。
2. .NET 可移植性分析
在升級前,保險起見需要使用.NET 可移植性分析器分析項目在目標.NET平臺上的可移植性。安裝.NET Portability Analyzer這個Visual Studio的擴展後在Visual Studio的解決方案資源管理器
視窗選中要分析的項目,右鍵選擇“Analyze Project Portability”:
在結果視窗選擇“Open Report”:
結果將以Excel的方式顯示,像這種小項目一般不會出現什麼問題,圖個安心:
3. 遷移到 PackageReference NuGet 引用
引用了Nuget包的舊.NET Framework項目會將引用的Nuget信息記錄在packages.config文件中,例如在示例的項目中,這個文件的內容如下:
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.CodeAnalysis.FxCopAnalyzers" version="2.9.8" targetFramework="net45" developmentDependency="true" />
<package id="Microsoft.CodeAnalysis.VersionCheckAnalyzer" version="2.9.8" targetFramework="net45" developmentDependency="true" />
<package id="Microsoft.CodeQuality.Analyzers" version="2.9.8" targetFramework="net45" developmentDependency="true" />
<package id="Microsoft.NetCore.Analyzers" version="2.9.8" targetFramework="net45" developmentDependency="true" />
<package id="Microsoft.NetFramework.Analyzers" version="2.9.8" targetFramework="net45" developmentDependency="true" />
<package id="Microsoft.Xaml.Behaviors.Wpf" version="1.1.19" targetFramework="net45" />
</packages>
新的SDK-Style項目文件使用PackageReference節點記錄Nuget的引用信息,這樣做的好處包括精簡內容與以及不再需要額外的packages.config文件,所以我們必須將packages.config遷移到 PackageReference。要遷移到PackageReference,先儘可能升級引用的Nuget包,然後選中項目中的packages.config,在右鍵菜單中選中“將 packages.config 遷移到 PackageReference”:
在彈出的對話框會列出頂級的依賴項和傳遞的依賴項,還會詢問是否將後者升級到頂級依賴項,這個項目無需做任何改變,直接點擊“確定”:
遷移完成後會得到一個報告:
打開Kino.Toolkit.Wpf.csproj,會發現少了些東西,但多了下麵這段,這段就是經過精簡的Nuget引用,在“管理Nuget程式包”的頁面也可以看到已安裝的Nuget變少了:
完成這一步後還原Nuget包,該升級的升級,運行下確認升級沒有出錯,然後進行下一步。
4. 遷移csproj項目文件
接下來需要遷移csproj項目文件到新的SDK-Style格式,不過在那以前好歹先確保自己已經安裝了.NET Core 3.1 SDK,隨便新建一個WPF (.NET Core)項目,這裡我選擇了自定義控制項庫項目:
生成的項目的csproj項目文件如下:
<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<UseWPF>true</UseWPF>
</PropertyGroup>
</Project>
其中SDK 是一組可生成 .NET Core 代碼的 MSBuild 任務和目標,Sdk="Microsoft.NET.Sdk.WindowsDesktop"
標識這是一個.NET Core的WinForms或WPF項目。
PropertyGroup
這一節表明這是個.NET Core 3.1項目,並使用WPF。如果是應用程式項目的話還需要<OutputType>WinExe</OutputType>
,因為這是個類庫項目所以缺少了這一節。
為了可以支持多個框架,需要將<TargetFramework>
這一節改為下麵內容,註意TargetFramework
變為TargetFrameworks
,因為從單一框架變成多個框架。
<TargetFrameworks>net462;netcoreapp3.1</TargetFrameworks>
現在可以把這些內容複製到Kino.Toolkit.Wpf.csproj,加上前面提到的<PackageReference>
節點的內容,完整內容如下:
<PropertyGroup>
<TargetFrameworks>net462;netcoreapp3.1</TargetFrameworks>
<UseWPF>true</UseWPF>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers">
<Version>2.9.8</Version>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Microsoft.Xaml.Behaviors.Wpf">
<Version>1.1.19</Version>
</PackageReference>
</ItemGroup>
</Project>
重新載入項目,還原Nuget包重新編譯等一系列操作都完成後,可以見到項目已經完成遷移了:
5. 處理其它問題
遷移項目文件後會有一些問題,首先是以前從項目中排除的文件又包含在項目里了,畢竟以前那麼複雜的項目文件可不是吃素的,這麼簡單粗暴遷移過來總會丟一些內容。重新將他們從項目中排除,項目文件多了以下這些內容,以表明這些文件都是多餘的(如果文件真是多餘的也可以直接刪掉):
<ItemGroup>
<Compile Remove="Class1.cs" />
<Compile Remove="SkeletonScreen\DispatcherContainer.cs" />
</ItemGroup>
<ItemGroup>
<None Remove="ClassDiagram1.cd" />
</ItemGroup>
AssemblyInfo.cs
這個文件有很多版本號之類的信息,現在都在項目文件中聲明,所以這些信息全都變得多餘,會引起編譯錯誤,全部刪掉只保留下麵這些就好:
// [assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)]
[assembly: ThemeInfo(
ResourceDictionaryLocation.None,
ResourceDictionaryLocation.SourceAssembly)
]
[assembly: XmlnsPrefix("https://github.com/DinoChan/Kino.Toolkit.Wpf", "kino")]
[assembly: XmlnsDefinition("https://github.com/DinoChan/Kino.Toolkit.Wpf", "Kino.Toolkit.Wpf")]
[assembly: XmlnsDefinition("https://github.com/DinoChan/Kino.Toolkit.Wpf", "Kino.Toolkit.Wpf.Primitives")]
其中ThemeInfo
指示項目使用預設的Themes\Generic.xaml
主題文件,對WPF項目是必不可少。XmlnsPrefix
等內容是為了方便在XAML內引用這個項目,具體可見命名空間這一段內容。
然後重新填一填應用程式和打包信息,可以看到項目文件中多了不少內容:
<PropertyGroup>
<TargetFrameworks>net462;netcoreapp3.1</TargetFrameworks>
<UseWPF>true</UseWPF>
<ApplicationIcon>Assets\Images\kino.ico</ApplicationIcon>
<Version>1.6.0</Version>
<Copyright>Copyright © 2019</Copyright>
<PackageLicenseExpression>https://raw.githubusercontent.com/DinoChan/Kino.Toolkit.Wpf/master/LICENSE</PackageLicenseExpression>
<PackageProjectUrl>https://github.com/DinoChan/Kino.Toolkit.Wpf</PackageProjectUrl>
<PackageIcon>Logo.png</PackageIcon>
<RepositoryUrl>https://github.com/DinoChan/Kino.Toolkit.Wpf</RepositoryUrl>
<PropertyGroup>
<TargetFrameworks>net462;netcoreapp3.1</TargetFrameworks>
<UseWPF>true</UseWPF>
<ApplicationIcon>Assets\Images\kino.ico</ApplicationIcon>
<Version>1.6.0</Version>
<Copyright>Copyright © 2019</Copyright>
<PackageLicenseExpression></PackageLicenseExpression>
<PackageProjectUrl>https://github.com/DinoChan/Kino.Toolkit.Wpf</PackageProjectUrl>
<PackageIcon>Logo.png</PackageIcon>
<RepositoryUrl>https://github.com/DinoChan/Kino.Toolkit.Wpf</RepositoryUrl>
<PackageTags>WPF Control Toolkit Xaml</PackageTags>
<Description>A set of wpf toolkit.</Description>
<NeutralLanguage>en-US</NeutralLanguage>
</PropertyGroup>
具體的打包成Nuget的過程可以參考林德熙的這篇文章:
VisualStudio 使用新項目格式快速打出 Nuget 包
6. 結語
實際上WPF項目要遷移到.NET Core會複雜很多,目前我也只是在控制項庫上嘗試。但換成新SDK-Style項目格式沒什麼壞處,可以放手一拼(只要不我讓我負責任)。
有些項目可能還需要安裝Microsoft.Windows.Compatibility,更多的信息請看下麵給出的參考鏈接。
7. 參考
Migrating WPF Apps to .NET Core 3.0 - WPF _ Microsoft Docs
.NET Core 的 csproj 格式的新增內容 - .NET Core CLI _ Microsoft Docs
從 .NET Framework 移植到 .NET Core - .NET Core _ Microsoft Docs
將 Contoso Expenses 應用遷移到 .NET Core 3 _ Microsoft Docs
.NET 可移植性分析器 - .NET _ Microsoft Docs
將傳統 WPF 程式遷移到 DotNetCore 3.0 - hippieZhou - 博客園
將基於 .NET Framework 的 WPF 項目遷移到基於 .NET Core 3 - walterlv
VisualStudio 使用新項目格式快速打出 Nuget 包
解決從舊格式的 csproj 遷移到新格式的 csproj 格式 AssemblyInfo 文件值重覆問題
WPF 講講 Microsoft.NET.Sdk.WindowsDesktop 的原理