With the recent official release of .NET Core 3, I thought I'd take a stab at a fun little experiment. I love using Electron to create web-powered desktop apps. And I wanted to learn more about Blazor, the newest addition to the ASP.NET family. In this post I will show you exactly how to get started with Blazor and Electron in under 15 minutes!
UPDATE: This post is featured in C# Digest #284 and I will be giving a talk about Desktop Apps with Blazor in Baltimore on November 20th!
Prerequisites
Be sure to install both the .NET Core SDK and Node.js (you can check versions using dotnet --version
and node --version
). We need .NET Core 3.0 because we're going to scaffold out the Blazor application using dotnet new
. We also need .NET Core 3.0 for Electron.NET, the library that allows for Electron & .NET integration. Node.js is used to download, configure, and integrate with the actual Electron instance.
Setup
Setup requires a few simple steps. While most steps are simply commands, there are 2 files that require manual edits. And once your project is setup, there's just one command to start it up!
Commands
mkdir blazor-electron-demo
cd blazor-electron-demo
dotnet new blazorserver --no-https
dotnet add package ElectronNET.API
dotnet new tool-manifest
dotnet tool install ElectronNET.CLI
dotnet electronize init
That's quite the block of commands, but they are all pretty simple and don't require any user interaction. Let's breakdown what each command is doing:
|
Creates a folder for our project and sets it as the current working directory. Be sure to change this to your preferred project name! |
|
Scaffolds out a new server-side Blazor app, without support for HTTPs. |
|
Installs the NuGet package that adds Electron support to ASP.NET apps. |
|
Adds support for locally installed `dotnet` tools. |
|
Installs the required `electronize` tool locally to the project. |
|
Sets up Electron.NET manifest and updates launch profiles. |
Sets up Electron.NET manifest and updates launch profiles. All commands should be executed in order and one at a time. Once they all complete, you are just two file changes away from running the application!
File Changes
In order for the ASP.NET application (Blazor app, in this case) to integrate with Electron, we need to make changes to Program.cs
and Startup.cs
.
Inside Program.cs
:
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using ElectronNET.API; // ADD THIS!
namespace blazor_electron_demo
{
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>()
.UseElectron(args); // ADD THIS!
});
}
}
Inside Startup.cs
:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using blazor_electron_demo.Data;
using ElectronNET.API; // ADD THIS!
namespace blazor_electron_demo
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddRazorPages();
services.AddServerSideBlazor();
services.AddSingleton<WeatherForecastService>();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment()) {
app.UseDeveloperExceptionPage();
} else {
app.UseExceptionHandler("/Error");
}
app.UseStaticFiles();
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapBlazorHub();
endpoints.MapFallbackToPage("/_Host");
});
// ADD THIS!
Task.Run(async () => await Electron.WindowManager.CreateWindowAsync());
}
}
}
With these two files updated we are now ready to run our app inside of Electron!
Usage
Run
Our app can be ran in two separate ways. Either with via the command line or through Visual Studio's "Run" dialog (thanks to the launch profile added during the setup using dotnet electronize init
).
dotnet electronize start
Debug
Once running, you can attach the debugger from Visual Studio's Debug > Attach to Process...
and filtering by the project name. Breakpoints will work exactly as expected when running a .NET app. You can even debug the code inside the Razor files! The only downside is when you need to make a change you'll have to stop the app, edit the changes, and restart it. We'll talk more about that a little later.
Publish
Thanks to Electron.NET, we can publish our application to a platform-specific installer in one command!
dotnet electronize build /target win
# OR
dotnet electronize build /target osx
# OR
dotnet electronize build /target linux
Windows Note: I did experience an exception about
The given key 'target' was not present in the dictionary.
while running the command from amingw64
prompt. Try the command again using a nativecmd
orpowershell
prompt.
This command will create a new installer under the ./bin/desktop
folder. Inside you will find the installer for your platform!
Sample Code
I've created a GitHub repo demonstrating all of the changes we just made. Use it as a resource if you are having trouble, or reach out to me on Twitter!
Caveats
While this union of two technologies is fun, it does have its drawbacks. Ranging from developer workflow issues to API mismatch, these issues may be annoying but they are by no means a deal-breaker to using Blazor and Electron.
Developer Workflow
The most severe issue is the slow iterative development process. At this time the developer workflow involves starting the app, manually attaching the debugger, testing the app, then stopping the app to make changes and finally restart the cycle. Without the ability to hot reload after changes this makes development significantly more difficult. The maintainers of Electron.NET recommend developing the app in the browser for the majority of tasks, and only switch to electron for periodic testing.
Electron API/Interop
Electron.NET is young but comprehensive. Creating a functional bridge from C# & .NET to JavaScript & Node.js/Electron is no small feat. But there is a high chance that you will find a method or event that is not implemented. Remember that Electron.NET is a free library created by people in their spare time. Every single feature may not be present, but that's where open source shines. If you found a hole in the API or a feature that needs some work, then fork the project and try finding the fix yourself. Most of the time building software is a collaborative effort, and it can be very rewarding to help solve the issues rather than just waiting for them to be fixed.
Summary
We created a brand new Blazor project, added support for Electron thanks to Electron.NET, and created an installer ready for release! While some caveats do exist when building with a dev stack this cutting edge, there is absolutely nothing stopping you from building incredible desktop applications using Blazor. And with help from developers like you, Electron.NET will continue to improve!
Special shout out to Rob Horner and John Sedlak for his help editing this article! Thanks Rob and John!
As always, if you liked the article let me know on Twitter! If you had any issues or didn't understand something, let me know that too! I truly appreciate the feedback!