歡迎光臨
每天分享高質量文章

.NET Core 3.0中的WinForms創建集中式拉取請求中心

Windows 窗體(或簡稱 WinForms),多年來被用於開發具有豐富和交互式界面的基於 Windows 的強大應用程式。

各類企業對這些桌面應用程式的投入量非常巨大,每月有大約 240 萬開發人員使用 Visual Studio 創建桌面式應用。

利用和擴展現有WinForms代碼資產的好處無疑極具吸引力,但還有其他好處。

WinForms 拖放式設計器體驗使用戶能夠構建功能齊全的 UI,而不需要任何特殊知識或培訓。

WinForms 應用程式易於部署和更新,可獨立於 Internet 連接工作,並且可以在不向 Internet 公開配置的本地計算機上運行,提高了安全性。

一直到最近,WinForms 應用程式都還依然只能使用完整的 .NET Framework 進行構建,但 .NET Core 3.0 預覽版的發佈改變了這一現狀。

.NET Core 的新功能和優點不再局限於 Web 開發。

通過.NET Core 3.0 WinForms增加了一些功能,比如更易於部署、更高的性能、對.NET Core 特有的NuGet 包的支持、.NET Core 命令列接口 (CLI) 等等。

本文將介紹使用這些功能的諸多好處、它們的重要性以及如何在WinForms應用程式中使用它們。

讓我們直接開始構建第一個 .NET Core 3.0 WinForms 應用程式。

在本文中,我將構建一個應用程式,用於檢索並顯示托管在 GitHub 上的其中一個開源 Microsoft 儲存庫的開放式拉取請求。

第一步是安裝最新版本的 Visual Studio 2019 和 .NET Core 3.0 SDK,之後便可使用 .NET Core CLI 命令來創建新的 WinForms 應用程式。

在添加 .NET Core 支持之前,WinForms 應用程式無法實現這一點。

即將發佈的是一個新的 Visual Studio 模板,用於創建針對 .NET Core 3.0 的 WinForms 專案。

由於模板目前尚未發佈,因此現在讓我們通過運行以下命令生成一個名為 PullRequestHub 的新 WinForms 專案:

dotnet new winforms -o PullRequestHub

為了確保專案成功創建,請導航到 dotnet new 命令創建的新目錄,使用 CLI 構建並運行專案,如下所示:

cd .\PullRequestHub\

由於可以訪問 .NET Core CLI,因此也可以訪問要還原、運行和構建的命令。在運行之前,請嘗試還原和構建命令,如下所示:

dotnet restore
dotnet build

這些命令的工作方式與在 .NET Core Web 應用程式的命令列中運行時的工作方式相同。請註意,在執行 dotnet run 命令時,它實際上會在執行應用之前執行還原和構建命令 (bit.ly/2UCkEaN)。現在讓我們運行專案,通過在命令列輸入 dotnet run 對其進行測試。

成功!你剛剛創建了第一個 .NET Core WinForms 應用程式。

運行時,將看到屏幕上出現一個帶有“Hello .NET Core!”文本的窗體。

在進一步嚮應用程式添加邏輯之前,讓我們花一點時間來討論 Visual Studio 中 WinForms 設計器視圖的當前狀態。

設置 .NET Core WinForms 應用的設計器

在 Visual Studio 中打開 CLI 生成的專案時,可能會註意到缺少某些功能。最值得註意的是,目前沒有為 .NET Core WinForms 應用程式提供設計器視圖。雖然有計劃提供此功能,但尚未完成。

幸運的是,有一個解決方案可讓你至少在添加本機支持之前訪問設計器。現在,可創建包含 UI 檔案的 .NET Framework 專案。

通過這種方式就可以使用設計器編輯 UI 檔案,然後 .NET Core 專案將取用 .NET Framework 專案中的 UI 檔案。這使你能夠利用 UI 功能,同時仍然在 .NET Core 中構建應用程式。

以下是我為專案執行操作的方法。

除了所創建的 PullRequestHub 專案之外,還需要添加一個在 .NET Full-Framework 版本上運行的新 WinForms 專案。

將此專案命名為 PullRequestHub.Designer。創建新專案後,從 .NET Core 專案中刪除 Form1 檔案,只保留 Program.cs 類。

導航到 PullRequestHub.Designer 並將窗體檔案重命名為 PullRequestForm。

現在將編輯 .NET Core 專案檔案,並添加以下代碼,將兩個專案中的檔案關聯起來。這還將負責處理將來創建的任何其他窗體或資源:


<Compile Include=”..\PullRequestHub.Designer\**\*.cs” />
ItemGroup>

儲存專案檔案後,將看到 PullRequestForm 檔案出現在解決方案資源管理器中,你將能夠與它們進行交互。

如果要使用 UI 編輯器,需要確保從 .NET Core 專案中關閉 PullRequestForm 檔案,並從 .NET Framework 專案中打開 PullRequestForm 檔案。

更改將在兩者中進行,但僅 .NET Framework 專案提供編輯器。

構建應用程式

讓我們開始嚮應用程式中添加一些代碼。為了從 GitHub 檢索開放式拉取請求,我需要創建一個 HttpClient。

這就是 .NET Core 3.0 的用武之地,因為它提供了對新 HttpClientFactory 的訪問權限。全框架版本中的 HttpClient 存在一些問題,包括使用 using 陳述句創建客戶端的問題。HttpClient 物件將被釋放,但底層套接字在一段時間內不會被釋放,預設情況下為 240 秒。如果套接字連接保持打開狀態 240 秒,並且系統中的吞吐量很高,則系統可能會使所有空閑套接字達到飽和。發生這種情況時,新請求必須等待套接字釋放,這可能對性能產生一些非常嚴重的影響。

HttpClientFactory 有助於緩解這些問題。首先,它提供了一種在更中心位置預先配置客戶端實現的更簡單方法。它還為你管理 HttpClients 的生命周期,因此你不會遇到前面提到的問題。我們來瞭解如何在 WinForms 應用程式中執行此操作。

使用此新功能的最佳和最簡單的方法之一是通過依賴項註入。依賴項註入,或更普遍的控制反轉,是一種將依賴項傳遞到類中的技術。它也是減少類耦合和簡化單元測試的絕佳方法。例如,你將看到如何在程式啟動時創建 IHttpClientFactory 的實體,以便稍後在窗體中使用該物件。在以前的 .NET 版本中,這在 WinForms 中不太容易實現,這就是使用 .NET Core 的另一個優勢。

在 Program.cs 中,將創建一個名為 ConfigureServices 的方法。在本方法中,創建新的 ServiceCollection,以便可通過依賴項註入來使用服務。

需要先安裝最新的這兩個 NuGet 包:

Microsoft.Extensions.DependencyInjection
Microsoft.Extensions.Http

然後添加圖 1 中所示的代碼。

這將創建一個要在窗體中使用的新 IHttpClientFactory。

結果會得到一個客戶端,可以顯式地使用它來處理涉及 GitHub API 的請求。

圖 1 創建新的 IHttpClientFactory

private static void ConfigureServices()
{
var services = new ServiceCollection();
services.AddHttpClient();
services.AddHttpClient(“github”, c =>
{
c.BaseAddress = new Uri(“https://api.github.com/”);
c.DefaultRequestHeaders.Add(“Accept”, “application/vnd.github.v3+json”);
c.DefaultRequestHeaders.Add(“User-Agent”, “HttpClientFactory-Sample”);
c.DefaultRequestHeaders.Add(“Accept”, “application/json”);
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
});
}

接下來,需要將實際的窗體類 PullRequestForm 註冊為單一實體。在本方法的末尾處,添加以下行:

services.AddSingleton<PullRequestForm>();

然後,需要創建 ServiceProvider 的實體。在 Program.cs 類的頂部,創建以下屬性:

private static IServiceProvider ServiceProvider { get; set; }

現在有了 ServiceProvider 屬性,請在 ConfigureServices 方法的末尾處添加一行來構建 ServiceProvider,如下所示:

ServiceProvider = services.BuildServiceProvider();

最後,完整的 ConfigureServices 方法應類似於圖 2 中的代碼。

圖 2 ConfigureServices 方法

private static void ConfigureServices()
{
var services = new ServiceCollection();
services.AddHttpClient();
services.AddHttpClient(“github”, c =>
{
c.BaseAddress = new Uri(“https://api.github.com/”);
c.DefaultRequestHeaders.Add(“Accept”, “application/vnd.github.v3+json”);
c.DefaultRequestHeaders.Add(“User-Agent”, “HttpClientFactory-Sample”);
c.DefaultRequestHeaders.Add(“Accept”, “application/json”);
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
});
services.AddSingleton();
ServiceProvider = services.BuildServiceProvider();
}

現在,需要在啟動時將窗體與容器連接起來。

應用程式運行時,這將呼叫 PullRequestForm 並提供可用的必要服務。

將 Main 方法更改為以下代碼:

[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
ConfigureServices();
Application.Run((PullRequestForm)ServiceProvider.GetService(typeof(PullRequestForm)));
}

感覺很棒!現在已完成所有連接。在 PullRequestForm 建構式中,註入剛連接好的 IHttpClientFactory 並將其分配給本地變數,如以下代碼所示:

private static HttpClient _httpClient;
public PullRequestForm(IHttpClientFactory httpClientFactory)
{
InitializeComponent();
_httpClient = httpClientFactory.CreateClient(“github”);
}

現在有了 HttpClient,可使用它來呼叫 GitHub 以檢索拉取請求、問題等。這也使得後續幾個步驟略微棘手。來自 HttpClient 的呼叫將為異步請求,如果你一直在使用 WinForms,你就知道接下來該怎麼做。接下來必須處理執行緒,並將調度更新發送到 UI 執行緒。

為了開始檢索所有拉取請求,需在視圖中添加一個按鈕。通過這種方式,可在將來添加更多儲存庫或更多儲存庫組進行檢查。使用連接的設計器,將按鈕拖到窗體上,並將文本重命名為“Microsoft”。

在此過程中,給按鈕取一個更具含義的名稱,如 RetrieveData_Button。需要系結到 RetrieveData_Button_Click 事件,但需要使用此代碼將其設置為異步:

private async void RetrieveData_Button_Click(object sender, EventArgs e)
{
}

在此處,需要呼叫檢索開放式 GitHub 拉取請求的方法。但首先,由於現在正在處理異步呼叫,因此必須連接 SynchronizationContext。可通過添加新屬性並使用以下代碼更新建構式來完成此操作:

private static HttpClient _httpClient;
private readonly SynchronizationContext synchronizationContext;
public PullRequestForm(IHttpClientFactory httpClientFactory)
{
InitializeComponent();
synchronizationContext = SynchronizationContext.Current;
_httpClient = httpClientFactory.CreateClient(“github”);
}

接下來,創建一個模型並將其命名為 PullRequestData,以便可以輕鬆地反序列化請求。相關代碼如下:

public class PullRequestData
{
public string Url { get; set; }
public string Title { get; set; }
}

最後,創建一個名為 GetPullRequestData 方法。在本方法中,將向 GitHub API 發出請求並檢索所有開放式拉取請求。將反序列化 JSON 請求,因此請將最新版本的 Newtonsoft.Json 包添加到專案中。

代碼如下:

private async Task> GetPullRequestData()
{
var gitHubResponse =
await _httpClient.GetStringAsync(
$”repos/dotnet/winforms/pulls?state=open”);
var gitHubData =
JsonConvert.DeserializeObject>(gitHubResponse);
return gitHubData;
}

現在可使用 RetrieveData_Button_Click 方法呼叫此方法。獲得所需的資料串列後,為每個標題創建標簽串列,以便在窗體上顯示。

獲得標簽串列後,可在 UpdateUI 方法中將它們添加到 UI 中。

圖 3 顯示了此步驟。

圖 3 從 RetrieveData_Button_Click 進行呼叫

private async void RetrieveData_Button_Click(object sender, EventArgs e)
{
var pullRequestData = await GetPullRequestData();
await Task.Run(() =>
{
var labelsToAdd = new List

然後,UpdateUI 方法將使用 synchronizationContext 更新 UI,如下所示:

public void UpdateUI(List)
{
synchronizationContext.Post(new SendOrPostCallback(o =>
{
foreach (var label in labels)
{
Controls.Add(label);
}

}), labels);

}

如果運行應用程式並單擊 Microsoft 按鈕,UI 將和 GitHub 上 dotnet/winforms 儲存庫中的所有開放式拉取請求的窗體一起更新。

現在輪到你了。正如本文的標題所說,為了使它成為一個真正的集中式拉取請求中心,讓我們更新此示例,以便從多個 GitHub 儲存庫中進行讀取。

這些儲存庫不需要來自 Microsoft 團隊,儘管觀察它們的行程十分有趣。

例如,微服務體系結構非常常見,在其中你可能擁有許多組成整個系統的儲存庫。鑒於一般而言,不長時間將分支和拉取請求單獨儲存是一個好主意,這樣的工具可以提高對開放式拉取請求的見解並提高整個系統的質量。

可設置一個 Web 應用,但又得擔心部署、運行、身份驗證等問題。

使用 . NET Core 中的 WinForms 應用程式,你無需擔心任何此類問題。

現在讓我們來看看使用 .NET Core 構建 WinForms 應用的最大優勢之一。

打包應用程式

過去,部署新的或更新的 WinForms 應用程式可能會導致與主機上安裝的 .NET Framework 版本相關的問題。通過 .NET Core 則可以獨立部署應用並從單個檔案夾運行應用,而不依賴於計算機上安裝的 .NET Framework 版本。

這意味著用戶無需安裝任何內容;他們可以僅運行應用程式。通過 .NET Core 還可以一次更新和部署一個應用,因為其包版本不會相互影響。

對於本文中的示例應用,需要為其獨立打包。請註意,獨立應用程式會更大,因為它們包含 .NET Core 庫。如果要部署到安裝了 .NET Core 最新版本的計算機中,則無需獨立部署應用。相反,可通過利用已安裝的 .NET Core 版本來減小已部署應用的大小。當不希望應用程式依賴於它將運行的環境時,可使用獨立選項。

若要在本地打包應用程式,需要確保在設置中啟用了開發人員樣式。嘗試運行打包專案時,Visual Studio 將進行提示並提供設置的鏈接,但若要直接啟用它,請轉到 Windows 設置,按 Windows 徽標鍵並搜索“設置”。在搜索框中鍵入“面向開發人員的設置”並選擇它。將看到啟用開發人員樣式的選項。選擇並啟用此選項。

大多數情況下,如果以前打包過 WinForms 應用程式,那麼創建獨立包的步驟則看起來比較熟悉。首先,創建一個新的 Windows 應用程式打包專案。將新專案命名為 PullRequestHubPackaging。當系統提示選擇標的和最低平臺版本時,請使用預設值並單擊“確定”。右鍵單擊應用程式併為 PullRequestHub 專案添加取用。

添加取用後,需要將 PullRequestHub 專案設置為入口點。完成後,在下次構建時很可能會看到以下錯誤:“如果 SelfContained 為 true,則專案 PullRequestHub 必須在專案檔案中指定 RuntimeIdentifiers。”

若要修複此錯誤,請編輯 PullRequestHub.csproj 檔案。在打開本專案檔案時,你會註意到使用 .NET Core 的另一個優點,這是因為該專案檔案現在使用的是新的輕量級格式。在基於 .NET Framework 的 WinForms 專案中,專案檔案將更詳細地包含顯式預設值和取用,並將 NuGet 取用拆分為 packages.config 檔案。新的專案檔案格式會將包取用引入到專案檔案中,從而可以在一個位置管理所有依賴項。

在本檔案的第一個 PropertyGroup 節點中,添加以下行:

<RuntimeIdentifiers>win-x86RuntimeIdentifiers>

運行時識別符號用於標識運行應用程式的標的平臺,並由 .NET 包用來表示 NuGet 包中特定於平臺的資產。

添加該識別符號後,構建就應該成功了,可將 PullRequestHubPackaging 專案設置為 Visual Studio 中的啟動專案。

指示專案是獨立的設置是 PullRequestHubPackaging.wapproj 檔案中需要註意的一點。檔案中需要註意的代碼部分如下:

<ItemGroup>
<ProjectReference Include=”..\PullRequestHub\PullRequestHub.csproj”>
<DesktopBridgeSelfContained>TrueDesktopBridgeSelfContained>


<DesktopBridgeIdentifier>$(DesktopBridgeRuntimeIdentifier)
DesktopBridgeIdentifier>
<Properties>SelfContained=%(DesktopBridgeSelfContained);
RuntimeIdentifier=%(DesktopBridgeIdentifier)
Properties>
<SkipGetTargetFrameworkProperties>True
SkipGetTargetFrameworkProperties>
ProjectReference>
ItemGroup>

在此處,可看到 DesktopBridgeSelfContained 選項已設置為 true,這使得 WinForms 應用程式可以與 .NET Core 二進制檔案一起打包。

運行該專案時,它會將檔案轉儲到“win-x86”的檔案夾中,該檔案夾的路徑與此類似:

C:\Your-Path\PullRequestHub\PullRequestHub\bin\x86\Debug\netcoreapp3.0

在 win-x86 檔案夾中你會註意到有許多 DLL,這些 DLL 包含獨立應用運行所需的所有檔案。

你更有可能需要將應用部署為旁載應用程式或將其上載到 Microsoft Store。旁載將使用 appinstaller 檔案進行自動更新。

從Visual Studio 2017 Update 15.7 開始支持這些更新。

此外,還可以創建支持提交到 Microsoft Store 進行分發的包。

Microsoft Store 隨後會處理應用的所有代碼簽名、分發和更新。

除了這些選項之外,還有一些正在進行的工作可以將應用程式打包成單個可執行檔案,從而無需使用 DLL 填充輸出目錄。

其他優點

通過 .NET Core 3.0,還可以利用 C# 8.0 的功能,包括可為 null 的取用型別、接口上的預設實現、使用樣式切換陳述句的改進以及異步資料流。

若要啟用 C# 8.0,請打開 PullRequestHub.csproj 檔案並將以下行添加到第一個 PropertyGroup:

<LangVersion>8.0LangVersion>

使用 .NET Core 和 WinForms 的另一個優點是兩個專案均為開源。

這使得你可以訪問原始碼、提交 bug、共享反饋併成為貢獻者。

請查看 github.com/dotnet/winforms上的 WinForms 專案。

.NET Core 3.0 旨在為企業和公司在 WinForms 應用程式中進行的投資註入新的活力,而 WinForms 應用程式會繼續保持高效、可靠且易於部署和維護。

開發人員可以利用新的 .NET Core 特定類(如 HttpClientFactory)、採用 C# 8.0 功能(如可為 null 的取用型別)和打包獨立應用程式。

還可以訪問 .NET Core CLI 以及 .NET Core 附帶的所有性能改進。

已同步到看一看
赞(0)

分享創造快樂