[WPF]是時候將WPF控制項庫從.Net Framework升級到.NET Core 3.1
- 2020 年 4 月 8 日
- 筆記
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="SkeletonScreenDispatcherContainer.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
指示項目使用默認的ThemesGeneric.xaml
主題文件,對WPF項目是必不可少。XmlnsPrefix
等內容是為了方便在XAML內引用這個項目,具體可見命名空間這一段內容。
然後重新填一填應用程式和打包資訊,可以看到項目文件中多了不少內容:
<PropertyGroup> <TargetFrameworks>net462;netcoreapp3.1</TargetFrameworks> <UseWPF>true</UseWPF> <ApplicationIcon>AssetsImageskino.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>AssetsImageskino.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 – 部落格園