Skip to content

About

Build a cross-platform desktop application atop XAF Blazor with Middle-Tier Security, and Electron.NET, supporting Windows and macOS deployment from a single codebase.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

14 Commits

Folders and files

Repository files navigation

XAF - Use Blazor UI with Electron as a hybrid Windows/macOS Desktop Application

In this GitHub example, an XAF-powered Blazor app is wrapped inside a native "web view" container (Electron desktop shell) for the target OS and connected to our Middle Tier application server.

By employing strategies outlined in this example, you can deliver XAF-powered solutions that target both Windows and non-Windows operating systems (when WinForms/Windows or browser-based apps do not fully address your business needs). For end users, the hybrid desktop solution documented herein will feel and behave (performance-wise) no different than Microsoft Teams, Visual Studio Code, Outlook (new), and similar hybrid apps. This web-based approach can serve as an alternative to native WinForms/WPF/VCL/Avalonia desktop development ("write once, run everywhere").

Application run on Mac OS

Important

This example references ElectronNET.Core 0.6.0 and may not be updated for newer versions. Electron.NET is a third-party library and its setup instructions, APIs, and package names can change between releases. Before you integrate Electron.NET into your application, check the official Electron.NET documentation for current instructions.

Prerequisites

  • .NET SDK 8.0 or later (the example projects target net10.0)
  • Node.js 22.x or later (must be available in PATH)
  • XAF Blazor Server application with Middle Tier Security enabled. You can create this application using our Template Kit

Tip

Run the following command to check the installed Node.js version:

node --version

Note

The example solution uses SQL Server LocalDB (Windows only). To run the example on macOS, see Run the Example on macOS.

Implementation Details

Step 1. Add NuGet Packages

Add the following packages to the ApplicationName.Blazor.Server project:

dotnet add package ElectronNET.Core
dotnet add package ElectronNET.Core.AspNet

Ensure that the project file contains the following references:

<PackageReference Include="ElectronNET.Core" Version="0.6.0" />
<PackageReference Include="ElectronNET.Core.AspNet" Version="0.6.0" />

Note

ElectronNET.Core does not require the legacy electronize CLI tool or the electron.manifest.json file. The library configures the Electron host through MSBuild.

Step 2. Register Electron in the Application Builder

Open the ApplicationName.Blazor.Server/Program.cs file and add the following namespaces:

using ElectronNET.API;
using ElectronNET.API.Entities;

Register Electron services conditionally. Add the following code after the WebApplication.CreateBuilder method call, AddRazorComponents registration, and before the AddXaf method call:

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorComponents()
    .AddInteractiveServerComponents();

bool useElectron = builder.Configuration.GetValue<bool>("USE_ELECTRON");
if (useElectron) {
    builder.Services.AddElectron();
    builder.UseElectron(args, async () => {
        var options = new BrowserWindowOptions {
            Show = false,
            IsRunningBlazor = true,
        };
        if (OperatingSystem.IsWindows() || OperatingSystem.IsLinux()) {
            options.AutoHideMenuBar = true;
        }
        var browserWindow = await Electron.WindowManager.CreateWindowAsync(options);
        browserWindow.OnReadyToShow += () => browserWindow.Show();
    });
}

builder.Services.AddXaf(builder.Configuration, b => {
    // ...
});

The code referenced above performs the following:

  • IsRunningBlazor = true configures the Electron renderer so that the Blazor SignalR circuit works correctly.
  • Show = false in combination with the OnReadyToShow event handler prevents an empty window from being displayed while the XAF application loads.
  • The USE_ELECTRON check keeps default browser hosting unchanged. Do not call UseElectron if the setting is not specified.

Step 3. Resolve the App Component Name Conflict

The ElectronNET.API namespace contains an App type. After you add the using ElectronNET.API; directive, the App identifier conflicts with the Blazor root component and the build fails with a CS0104 error.

To resolve this conflict, specify the fully qualified name of the root component in the MapRazorComponents method call:

app.MapRazorComponents<ApplicationName.Blazor.Server.Components.App>()
    .AddInteractiveServerRenderMode()
    .AddAdditionalAssemblies(typeof(BlazorApplication).Assembly);

The rest of the XAF pipeline (AddXaf, Security System configuration, UseXaf, and MapXafEndpoints) does not require modification.

Disable HTTPS Redirection in Electron Mode

In Electron mode, the application listens to HTTP only. If you call UseHttpsRedirection, the middleware cannot determine the HTTPS port and logs a Failed to determine the https port for redirect warning. Call UseHttpsRedirection only if the application does not run in Electron mode:

if (!useElectron) {
    app.UseHttpsRedirection();
}

Traffic between Electron window and Kestrel does not leave the local machine, so HTTPS is not required. UseHsts does not need modification: HSTS middleware ignores localhost by default.

Step 4. Add an Electron Launch Profile

Open the ApplicationName.Blazor.Server/Properties/launchSettings.json file and add a profile that sets the USE_ELECTRON environment variable and disables browser launch:

"Electron": {
    "commandName": "Project",
    "dotnetRunMessages": true,
    "launchBrowser": false,
    "applicationUrl": "http://localhost:5000",
    "environmentVariables": {
    "ASPNETCORE_ENVIRONMENT": "Development",
    "USE_ELECTRON": "true"
  }
}

Step 5. Run the Application

  1. Start the ApplicationName.MiddleTierWebApi project (Middle Tier Security server).

    In the example solution, run the following command in the ElectronExample.MiddleTier folder:

    dotnet run --launch-profile ElectronExample.MiddleTier

    For hybrid Electron usage scenarios, we recommend that you always use XAF Blazor UI with the Middle Tier Security for secure communication between the frontend and backend, helping to protect sensitive data and enforce stricter access controls.

  2. Run the ApplicationName.Blazor.Server project with the Electron launch profile:

    • In Visual Studio or JetBrains Rider, select the Electron profile and start the project.
    • In the command line, run dotnet run --launch-profile Electron in the project folder. The first run restores and installs Electron npm packages, so it takes longer than subsequent runs.
  3. A desktop window opens and displays the XAF application's logon page. Use the Admin or User credential with an empty password to log in.

To run the application in a browser, use any other launch profile. The application then behaves as a standard XAF Blazor application.

Step 6. (Optional) Use Native Electron APIs from XAF Actions

In this setup, the XAF Blazor application and Electron shell run on the same machine. The .NET process hosts the Kestrel server, and Electron displays it in a native window. This configuration allows XAF Controllers to call the Electron.NET API (the static Electron class) and access native OS features that a standard browser cannot reach: message boxes, file dialogs, notifications, the system tray, the clipboard, etc. This setup works as follows:

  1. A user clicks an XAF Action in the Electron window.
  2. The Action's Execute event handler runs on the server (local .NET process), as in any XAF Blazor application.
  3. The handler calls an Electron.NET API, such as Electron.Dialog.ShowMessageBoxAsync.
  4. Electron.NET forwards the call to the Electron main process, which displays a native OS dialog.

Note

Native APIs only work when the application runs with the Electron launch profile. In a standard browser, the Electron process does not exist. Use the HybridSupport.IsElectronActive property to deactivate Electron-specific Actions in such instances.

Example: Show a Native Message Box

The ElectronExample.Blazor.Server/Controllers/ElectronMessageBoxController.cs file in the example solution implements a Window Controller with a Native Message Box (SimpleAction). The Action displays a native Electron message box:

using DevExpress.ExpressApp;
using DevExpress.ExpressApp.Actions;
using DevExpress.Persistent.Base;
using ElectronNET.API;
using ElectronNET.API.Entities;

namespace ApplicationName.Blazor.Server.Controllers;

public class ElectronMessageBoxController : WindowController {
    public ElectronMessageBoxController() {
        TargetWindowType = WindowType.Main;
        var showMessageBoxAction = new SimpleAction(this, "ShowElectronMessageBox", PredefinedCategory.Tools) {
            Caption = "Native Message Box",
            ImageName = "Action_AboutInfo"
        };
        showMessageBoxAction.Execute += ShowMessageBoxAction_Execute;
        Active["ElectronActive"] = HybridSupport.IsElectronActive;
    }

    private async void ShowMessageBoxAction_Execute(object sender, SimpleActionExecuteEventArgs e) {
        var options = new MessageBoxOptions("Hello from the native Electron message box!") {
            Type = MessageBoxType.info,
            Title = "ElectronExample",
            Buttons = new[] { "OK" }
        };
        try {
            var window = Electron.WindowManager.BrowserWindows.FirstOrDefault();
            if (window != null)
                await Electron.Dialog.ShowMessageBoxAsync(window, options);
            else
                await Electron.Dialog.ShowMessageBoxAsync(options);
        }
        catch (Exception ex) {
            Tracing.Tracer.LogError(ex);
        }
    }
}

How the Controller works:

  • Declare an XAF Action: The Controller creates a SimpleAction and handles its Execute event. XAF renders the Action in the toolbar. You do not need custom Blazor markup.
  • Activate the Controller in Electron mode only: Active["ElectronActive"] = HybridSupport.IsElectronActive hides the Action when the application runs in a standard browser, where native APIs are unavailable.
  • Call the native API in the Execute handler: Configure the dialog with MessageBoxOptions and pass it to Electron.Dialog.ShowMessageBoxAsync. Electron.NET forwards the call to the Electron main process, which displays the native OS dialog.
  • Pass the parent window: Electron.WindowManager.BrowserWindows.FirstOrDefault() returns the application window. When you pass it to ShowMessageBoxAsync, the dialog is modal to this window.
  • Handle exceptions: The handler is async void because the Execute event is synchronous. Wrap the code in try/catch and log errors with Tracing.Tracer.LogError. Otherwise, an unhandled exception can terminate the application.

To configure the dialog, use the Type (info, warning, error, question), Title, Buttons, DefaultId, CancelId, and Detail properties of MessageBoxOptions. ShowMessageBoxAsync returns a MessageBoxResult object. Its Response property contains the index of the clicked button. Use this value to implement a confirmation dialog:

var result = await Electron.Dialog.ShowMessageBoxAsync(window, new MessageBoxOptions("Delete the record?") {
    Type = MessageBoxType.question,
    Buttons = new[] { "Yes", "No" },
    CancelId = 1
});
if (result.Response == 0) {
    // The user clicked "Yes".
}

To test the Action, run the application with the Electron launch profile and click Native Message Box in the toolbar.

Other Native APIs

You can use the same pattern to call other Electron.NET APIs from XAF Controllers:

API Purpose
Electron.Dialog.ShowOpenDialogAsync / ShowSaveDialogAsync Native file open and save dialogs
Electron.Notification.Show OS notifications
Electron.Tray A system tray icon and menu
Electron.Clipboard Read and write the system clipboard
Electron.Shell.OpenExternalAsync Open a URL in the default browser
Electron.Menu.SetApplicationMenu A native application menu

Run the Example on macOS

The example solution runs on macOS (tested on Apple silicon) with the following changes.

Use SQLite Instead of LocalDB

The ElectronExample.MiddleTier/appsettings.json file specifies a SQL Server LocalDB connection string. LocalDB is only available on Windows. To use SQLite instead, follow the steps below:

  1. Add the Microsoft.Data.Sqlite package to the ElectronExample.MiddleTier project:

    dotnet add CS/ElectronExample.MiddleTier package Microsoft.Data.Sqlite
  2. Override the connection string with an environment variable when you start the Middle Tier server:

    cd CS/ElectronExample.MiddleTier
    ConnectionStrings__ConnectionString="XpoProvider=SQLite;Data Source=ElectronExample.db" \
      dotnet run --launch-profile ElectronExample.MiddleTier

    The server creates the database and demo users on the first run.

The Middle Tier server also listens to HTTPS and requires a trusted development certificate. If the certificate is missing, run dotnet dev-certs https --trust.

Start the Electron Application

In a separate terminal, run the following commands:

cd CS/ElectronExample.Blazor.Server
dotnet run --launch-profile Electron

To log in, use the Admin or User credential with an empty password.

See Also

Does This Example Address Your Development Requirements/Objectives?

(you will be redirected to DevExpress.com to submit your response)

About

Build a cross-platform desktop application atop XAF Blazor with Middle-Tier Security, and Electron.NET, supporting Windows and macOS deployment from a single codebase.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Contributors

Languages