PhotinoX.Blazor 5.2.2

Prefix Reserved
dotnet add package PhotinoX.Blazor --version 5.2.2
                    
NuGet\Install-Package PhotinoX.Blazor -Version 5.2.2
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="PhotinoX.Blazor" Version="5.2.2" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="PhotinoX.Blazor" Version="5.2.2" />
                    
Directory.Packages.props
<PackageReference Include="PhotinoX.Blazor" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add PhotinoX.Blazor --version 5.2.2
                    
#r "nuget: PhotinoX.Blazor, 5.2.2"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package PhotinoX.Blazor@5.2.2
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=PhotinoX.Blazor&version=5.2.2
                    
Install as a Cake Addin
#tool nuget:?package=PhotinoX.Blazor&version=5.2.2
                    
Install as a Cake Tool

PhotinoX Logo

PhotinoX.Blazor

NuGet Version Build License NuGet Downloads

Blazor integration for PhotinoX desktop applications.

PhotinoX.Blazor extends the application model provided by PhotinoX.App with support for running Blazor applications in native Photino windows, including root components, static web resources, URL loading policies, and multiple Blazor windows.

  • Windows: WebView2
  • macOS: WKWebView
  • Linux: WebKitGTK 4.1

PhotinoX.Blazor is an independent fork of tryphotino/photino.Blazor under the Apache-2.0 license and is not affiliated with the original project or organization.

Package architecture

The PhotinoX packages form a layered application stack:

PhotinoX.Native
└── PhotinoX
    └── PhotinoX.App
        └── PhotinoX.Blazor
  • PhotinoX.Native provides the native window and WebView runtime.
  • PhotinoX provides the managed application, dispatcher, window, and WebView APIs.
  • PhotinoX.App adds application composition, configuration, services, and lifetime management.
  • PhotinoX.Blazor builds on PhotinoX.App and adds Blazor-specific application and window hosting.

PhotinoBlazorApp is a Blazor facade over PhotinoApp. Common application services, configuration, environment information, logging, application initialization, and disposal are provided by PhotinoX.App.

Features

  • Modern PhotinoBlazorApp.CreateBuilder(...) application startup
  • Dependency injection through IServiceCollection
  • Configuration through ConfigurationManager
  • Logging through ILoggingBuilder
  • Environment information through PhotinoEnvironment
  • Blazor root components
  • Configurable host page and application base URI
  • Physical, embedded, composite, or custom IFileProvider
  • Main-window configuration before WebView initialization
  • Default, main-window, and named-window settings
  • Multiple independent Blazor windows
  • Per-window service scope, dispatcher, synchronization context, and WebView manager
  • Blazor WebView-style URL loading policy
  • Synchronous and asynchronous application disposal
  • Unified app custom scheme on Windows, macOS, and Linux

Quick start

using Photino.Blazor;

namespace MyApp;

internal static class Program
{
    private static async Task<int> Main(string[] args)
    {
        var builder = PhotinoBlazorApp.CreateBuilder(args);

        builder.RootComponents.Add<App>("#app");

        builder.ConfigureMainWindow(window =>
        {
            window
                .SetTitle("My PhotinoX Blazor App")
                .SetSize(1200, 800);
        });

        await using var app = builder.Build();

        return app.Run();
    }
}

Build() creates the application, root service provider, and main PhotinoBlazorWindow. The native window is initialized when the application is run.

Run() starts the main Blazor content and runs the native application message loop. It does not dispose the application after the message loop exits. The caller must dispose the application through using, await using, Dispose(), or DisposeAsync().

On Windows, PhotinoApplication.Run() performs native window initialization and runs the message loop on an STA thread when the calling thread is not an STA thread. This allows PhotinoBlazorApp to be used from an asynchronous Main method.

Application builder

PhotinoBlazorAppBuilder is a Blazor facade over PhotinoAppBuilder.

It exposes:

  • Services
  • Configuration
  • Environment
  • Logging
  • RootComponents
  • ConfigureApplication(...)
  • ConfigureBeforeDispose(...)
  • ConfigureContainer(...)
  • ConfigureMainWindow(...)
  • ConfigureServices(...)
  • ConfigureBlazor(...)
  • UseFileProvider(...)
  • UseAppServicesInitialization(...)

Create a builder:

var builder = PhotinoBlazorApp.CreateBuilder(args);

Create a builder with explicit application options:

var builder = PhotinoBlazorApp.CreateBuilder(new PhotinoAppOptions
{
    Args = args,
    EnvironmentName = "Development",
    ContentRootPath = AppContext.BaseDirectory,
    WebRootPath = "wwwroot"
});

Disable the common PhotinoX.App defaults:

var builder = PhotinoBlazorApp.CreateBuilder(args, useDefaults: false);

useDefaults: false disables the common configuration sources, logging defaults, and PhotinoAppSettings binding. Required Blazor services are always registered.

Services

Register application services directly:

builder.Services.AddSingleton<MyService>();

Or use the fluent configuration API:

builder.ConfigureServices(services =>
{
    services.AddSingleton<MyService>();
    services.AddMudServices();
});

The built application exposes the root service provider:

await using var app = builder.Build();

var service = app.Services.GetRequiredService<MyService>();

Each PhotinoBlazorWindow uses a child service scope. The window service provider also supplies a window-specific HttpClient and IPhotinoWebResourceHandler, while other scoped application services are resolved from that child scope.

Custom service providers

Custom dependency injection containers are supported through IServiceProviderFactory<TContainerBuilder>.

For example, to use Autofac, install Autofac.Extensions.DependencyInjection:

dotnet add package Autofac.Extensions.DependencyInjection

Configure Autofac as the root service provider:

var builder = PhotinoBlazorApp.CreateBuilder(args);

builder.ConfigureContainer(
    new AutofacServiceProviderFactory(),
    container =>
    {
        container.RegisterModule<ApplicationModule>();
    });

builder.RootComponents.Add<App>("#app");

using var app = builder.Build();
return app.Run();

The configured provider is used for application services and as the foundation for per-window Blazor service scopes. Implementing IHostBuilder or manually building a separate container is not required.

Configuration and logging

PhotinoBlazorAppBuilder exposes the configuration, environment, and logging APIs from PhotinoAppBuilder:

builder.Configuration["PhotinoX:MainWindow:Window:Title"] = "My Blazor App";
builder.Logging.AddFilter("MyApp", LogLevel.Debug);

With defaults enabled, configuration is loaded from:

  • appsettings.json
  • appsettings.{EnvironmentName}.json
  • environment variables
  • command-line arguments

The built application exposes the same application state:

var configuration = app.Configuration;
var environment = app.Environment;
var services = app.Services;
var application = app.Application;
var dispatcher = app.Dispatcher;

Blazor options

PhotinoBlazorOptions configures the Blazor WebView host:

builder.ConfigureBlazor(options =>
{
    options.AppBaseUri = new Uri("app://localhost/");
    options.HostPage = "index.html";
});

The default values are:

AppBaseUri = app://localhost/
HostPage   = index.html

With defaults enabled, options can also be configured through the PhotinoX:Blazor configuration section:

{
  "PhotinoX": {
    "Blazor": {
      "AppBaseUri": "app://localhost/",
      "HostPage": "index.html"
    }
  }
}

AppBaseUri must be an absolute URI, and HostPage must not be empty.

Static web resources

By default, PhotinoX.Blazor creates a PhysicalFileProvider for PhotinoEnvironment.WebRootPath.

Configure the web root through application options:

var builder = PhotinoBlazorApp.CreateBuilder(new PhotinoAppOptions
{
    Args = args,
    WebRootPath = "wwwroot"
});

Replace the default file provider:

builder.UseFileProvider(_ =>
    new ManifestEmbeddedFileProvider(typeof(Program).Assembly, "wwwroot"));

The provider factory receives the built root service provider:

builder.UseFileProvider(services =>
{
    var environment = services.GetRequiredService<PhotinoEnvironment>();
    return new PhysicalFileProvider(environment.WebRootPath);
});

An explicitly configured provider replaces the default PhysicalFileProvider.

Root components

Configure main-window root components before calling Build():

builder.RootComponents.Add<App>("#app");

Root component parameters are also supported:

builder.RootComponents.Add<App>("#app", new Dictionary<string, object?>
{
    ["Title"] = "PhotinoX"
});

At least one root component must be configured for the main window.

Main window configuration

Configure the main native window before its WebView is initialized:

builder.ConfigureMainWindow(window =>
{
    window
        .SetTitle("PhotinoX Blazor App")
        .SetSize(1400, 800)
        .SetDevToolsEnabled(true)
        .SetIconFile("favicon.ico");
});

Multiple callbacks are applied in registration order:

builder.ConfigureMainWindow(window => window.SetTitle("My App"));
builder.ConfigureMainWindow(window => window.SetSize(1200, 800));

The effective order is:

Built-in window defaults
→ PhotinoX:WindowDefaults
→ PhotinoX:MainWindow
→ ConfigureMainWindow callbacks

Window settings are applied before the Blazor WebView manager is created. This is important for browser initialization parameters, WebView2 user-data folders, browser security settings, and other initialization-time options.

Application configuration

Configure the underlying PhotinoApplication:

builder.ConfigureApplication(application =>
{
    application.ShutdownMode = PhotinoShutdownMode.OnMainWindowClose;

    application.ShutdownRequested += (_, e) =>
    {
        if (e.Reason == PhotinoShutdownRequestReason.Application)
        {
            // e.Cancel = true;
        }
    };
});

PhotinoX.Blazor configures OnMainWindowClose as its default shutdown mode. User callbacks are applied afterward and can override that value.

Application lifetime

PhotinoBlazorApp.Run() starts the main Blazor renderer and delegates the native lifetime to PhotinoApp.Run() and PhotinoApplication.Run().

Use synchronous disposal:

using var app = builder.Build();
return app.Run();

Use asynchronous disposal when the application or registered services require asynchronous cleanup:

await using var app = builder.Build();
return app.Run();

Register code that must run before Blazor windows and application services are disposed:

builder.ConfigureBeforeDispose(app =>
{
    var service = app.Services.GetRequiredService<MyService>();
    service.SaveState();

    app.MainWindow.Log("Application is shutting down.");
});

Callbacks execute in registration order while the Blazor windows and root service provider are still available.

If a callback throws, subsequent callbacks are not invoked. Blazor windows and the root service provider are still disposed.

Application and window model

PhotinoX.Blazor separates application-level services from window-level Blazor hosting:

  • PhotinoBlazorApp is the Blazor application facade over PhotinoApp.
  • PhotinoBlazorWindow represents one native PhotinoWindow hosting Blazor content.
  • Each PhotinoBlazorWindow has its own root components, WebView manager, Blazor dispatcher and synchronization context, service scope, resource handler, and message pipeline.
  • Application services, configuration, logging, environment information, and the native application lifetime are shared through PhotinoApp.

This architecture allows multiple Blazor windows without sharing renderer-specific state between native windows.

Multiple windows

Create a secondary window:

var window = app.CreateWindow();

window.RootComponents.Add<Settings>("#app");

window.Window.SetTitle("Settings");
window.Show();

Create a secondary window and add its root component in one call:

var window = app.CreateWindow<Settings>("#app");
window.Show();

Configure a window before its WebView is initialized:

var window = app.CreateWindow<Settings>(
    "#app",
    configure: nativeWindow =>
    {
        nativeWindow
            .SetTitle("Settings")
            .SetSize(700, 500);
    });

window.Show();

Window configuration callbacks must be used for settings that affect WebView initialization.

Named window configuration

Configure named windows in appsettings.json:

{
  "PhotinoX": {
    "WindowDefaults": {
      "Window": {
        "Width": 900,
        "Height": 600,
        "CenterOnInitialize": true
      }
    },
    "Windows": {
      "Settings": {
        "Window": {
          "Title": "Settings",
          "Width": 700,
          "Height": 500,
          "StartUrl": "/settings"
        },
        "Browser": {
          "DevToolsEnabled": true
        }
      }
    }
  }
}

Apply the named configuration:

var window = app.CreateWindow<Settings>(
    "#app",
    configurationName: "Settings");

window.Show();

The effective configuration is:

WindowDefaults + Windows[Settings] + configure callback

For Blazor windows, relative startup URLs are passed to the Blazor WebView as application routes. They are not resolved as physical paths through PhotinoEnvironment.WebRootPath.

Application scheme

PhotinoX.Blazor uses the app custom scheme on Windows, macOS, and Linux:

app://localhost/

The previous upstream Windows http workaround is not used. PhotinoX.Native supports custom-scheme registration and navigation in WebView2.

URL loading

PhotinoBlazorWindow.UrlLoading provides a Blazor WebView-style policy for top-level URL navigation:

app.MainBlazorWindow.UrlLoading += (_, e) =>
{
    if (e.Url.Host.Equals("blocked.example.com", StringComparison.OrdinalIgnoreCase))
    {
        e.UrlLoadingStrategy = UrlLoadingStrategy.CancelLoad;
        return;
    }

    if (e.Url.Scheme == Uri.UriSchemeHttp || e.Url.Scheme == Uri.UriSchemeHttps)
        e.UrlLoadingStrategy = UrlLoadingStrategy.OpenExternally;
};

Default behavior:

  • URLs within the application base URI load inside the WebView.
  • URLs outside the application base URI open through the operating system, typically in the default browser.
  • target="_blank" links and JavaScript window.open(...) requests open externally.
  • Navigation can be canceled through UrlLoadingStrategy.CancelLoad.

Available strategies:

UrlLoadingStrategy.OpenInWebView
UrlLoadingStrategy.OpenExternally
UrlLoadingStrategy.CancelLoad

External URLs should only be loaded inside the WebView when the content is trusted.

Showing windows

PhotinoBlazorWindow.Show() starts the Blazor renderer and shows the native window on the current thread.

On Windows, the current thread must be an STA thread when Show() initializes the native window for the first time.

For the main window, use:

app.Run();

PhotinoBlazorApp.Run() delegates native window creation and message-loop execution to PhotinoApplication.Run(), which provides automatic STA handling on Windows.

Secondary windows should be created and shown on the application UI thread. Use PhotinoBlazorApp.Dispatcher when window operations originate from another thread.

Key differences from the upstream Photino.Blazor project

Area Upstream Photino.Blazor PhotinoX.Blazor
Application model Independent Blazor-specific application and service model Blazor facade over PhotinoX.App
Application composition Blazor services configured independently Uses PhotinoAppBuilder, DI, configuration, logging, environment, and application initialization
Window hosting Main window and renderer services are mostly application-level Every PhotinoBlazorWindow owns window-specific renderer state and a service scope
Multiple windows Primarily designed around one application-level Blazor window Explicit multi-window model with independent root components and WebView managers
Application scheme Uses http on Windows and app on Linux/macOS Uses app on all supported platforms
URL loading policy No Blazor WebView-style policy for normal top-level navigation Provides UrlLoading, UrlLoadingEventArgs, and UrlLoadingStrategy
Disposal Application-specific cleanup Integrates with PhotinoApp synchronous and asynchronous disposal

Core (ecosystem)

  • PhotinoX - managed .NET wrapper around the native layer.
  • PhotinoX.App - application composition layer for PhotinoX desktop applications.
  • PhotinoX.Native - native binaries for Windows/macOS/Linux.
  • PhotinoX.Server - optional local static-file server for SPA/static assets.
  • PhotinoX.Samples - sample projects showcasing common scenarios.

Install

dotnet add package PhotinoX.Blazor

PhotinoX.Blazor depends on PhotinoX.App, which depends on PhotinoX. Platform-specific native binaries are provided by the PhotinoX runtime packages.

Package targets net8.0; net9.0; net10.0.

Samples

Requirements

Build from source

dotnet restore Photino.Blazor/PhotinoX.Blazor.csproj
dotnet build   Photino.Blazor/PhotinoX.Blazor.csproj -c Release
dotnet pack    Photino.Blazor/PhotinoX.Blazor.csproj -c Release -o artifacts

CI: see .github/workflows/build.yml (build + pack + upload .nupkg/.snupkg).

Contributing

Issues and PRs are welcome. Keep PRs focused, minimal, and consistent with the rest of PhotinoX.

License

PhotinoX.Blazor is licensed under Apache-2.0.

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 is compatible.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
5.2.2 184 9/19/2026
5.2.1 158 9/18/2026
5.2.0 161 9/16/2026
5.1.2 862 8/25/2026
5.1.1 210 8/23/2026
5.1.0 151 8/21/2026
5.0.3 344 8/17/2026
5.0.2 229 8/17/2026
5.0.1 151 8/15/2026
5.0.0 271 8/12/2026
5.0.0-preview.11 79 8/12/2026
5.0.0-preview.10 82 8/7/2026
5.0.0-preview.9 75 8/6/2026
5.0.0-preview.8 75 8/4/2026
5.0.0-preview.7 77 8/3/2026
5.0.0-preview.6 65 8/2/2026
5.0.0-preview.5 75 8/1/2026
5.0.0-preview.4 66 7/31/2026
5.0.0-preview.3 68 7/31/2026
5.0.0-preview.2 64 7/30/2026
Loading failed