Page 37 - MSDN Magazine, May 2019
P. 37
Success! You’ve just created your first .NET Core WinForms application. When you run, you’ll see a form appear on your screen with the text, “Hello .NET Core!”.
Before I go further with adding logic to our application, let’s take a moment to talk about the current state of the WinForms Designer view in Visual Studio.
Setting up the Designer
for .NET Core WinForms Apps
When you open the CLI-generated project in Visual Studio, you may notice that some functionality is missing. Most notably, there’s currently no designer view provided for .NET Core WinForms applications. While there are plans to make this functionality available, they’ve yet to be completed.
Fortunately, there’s a workaround that can give you access to a designer, at least until native support is added. For now, you can create a .NET Framework project that contains your UI files. This way you can edit the UI files using the designer, and the .NET Core project will then reference the UI files from the .NET Framework project. This enables you to leverage the UI capabilities while still building the application in .NET Core. Here’s how I do this for my project.
In addition to the PullRequestHub project you created, you’ll want to add a new WinForms project running on a version of .NET Full-Framework. Name this project PullRequestHub.Designer. After the new project is created, remove the Form1 files from the .NET Core project, leaving only the Program.cs class.
Navigate into the PullRequestHub.Designer and rename the form files to PullRequestForm. Now you’ll edit the .NET Core project file and add the following code to link the files together in both projects. This will take care of any additional forms or resources you create in the future, as well:
<ItemGroup>
<Compile Include=”..\PullRequestHub.Designer\**\*.cs” />
</ItemGroup>
Once you save the project file, you’ll see the PullRequestForm files appear in the solution explorer and you’ll be able to interact with them. When you want to use the UI editor, you’ll need to make sure to close the PullRequestForm file from the .NET Core project and open the PullRequestForm file from the .NET Frame- work project. The changes will take place in both, but the editor is only available from the .NET Framework project.
Building the Application
Let’s start adding some code to the application. In order to retrieve open pull requests from GitHub, I need to create an HttpClient. This is where .NET Core 3.0 comes in, because it provides access to the new HttpClientFactory. The HttpClient in the full-framework version had some issues, including one with creating the client with a using statement. The HttpClient object would be disposed of, but the underlying socket wouldn’t be released for some time, which by default is 240 seconds. If the socket connection remains open for 240 seconds and you have a high throughput in your system, there’s a chance your system saturates all the free sockets. When this happens, new requests must wait for a socket to free up, which can produce some rather drastic performance impacts.
The HttpClientFactory helps mitigate these issues. For one, it gives
you an easier way to pre-configure client implementations in a more central location. It also manages the lifetime of the HttpClients for you, so you don’t run into the previously mentioned issues. Let’s look at how you can do this now in a WinForms application.
One of the best and easiest ways to use this new feature is through dependency injection. Dependency injection, or more generally inversion of control, is a technique for passing dependencies into classes. It’s also a fantastic way to reduce the coupling of classes and to ease unit testing. For example, you’ll see how you can create an instance of the IHttpClientFactory while the program is starting up, with that object able to be leveraged later in the form. This was not something very easily accomplished in WinForms on previous versions of .NET, and it’s another advantage of using .NET Core.
In the Program.cs you’re going to create a method named ConfigureServices. In this method, create a new ServiceCollection to make services available to you via dependency injection. You’ll need to install the latest of these two NuGet packages first:
- 'Microsoft.Extensions.DependencyInjection' - 'Microsoft.Extensions.Http'
Then add the code shown in Figure 1. This creates a new IHttp- ClientFactory to be used in your forms. The result is a client that you can explicitly use for requests involving the GitHub API.
Next, you need to register the actual form class, PullRequestForm, as a singleton. To the end of this method, add the following line:
services.AddSingleton<PullRequestForm>();
Then you need to create an instance of the ServiceProvider. At the top of the Program.cs class, create the following property:
private static IServiceProvider ServiceProvider { get; set; }
Now that you have a property for your ServiceProvider, at the end of the ConfigureServices method, add a line to build the ServiceProvider, like so:
ServiceProvider = services.BuildServiceProvider();
At the end of all of this, the full ConfigureServices method should look like the code in Figure 2.
Now you need to wire the form up with the container when it starts. When the application runs, this will invoke the PullRequest- Form with the necessary services available to it. Change the Main method to be the following code:
[STAThread] static void Main() {
Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); ConfigureServices(); Application.Run((PullRequestForm)ServiceProvider.
GetService(typeof(PullRequestForm))); }
Figure 1 Create a New 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;
}); }
msdnmagazine.com
May 2019 31