Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/kizari/testudo

A work-in-progress experimental cross-platform library for creating lightweight desktop Blazor applications.
https://github.com/kizari/testudo

blazor desktop

Last synced: 3 months ago
JSON representation

A work-in-progress experimental cross-platform library for creating lightweight desktop Blazor applications.

Awesome Lists containing this project

README

        

# Testudo

A work-in-progress experimental cross-platform library for creating lightweight desktop Blazor applications.

## Table of contents

* [Features](#Features)
* [Getting started](#Getting-started)
* [Creating the project](#Creating-the-project)
* [Setting up dependency injection](#Setting-up-dependency-injection)
* [Creating the Blazor application](#Creating-the-Blazor-application)
* [Launching the application window](#Launching-the-application-window)

## Features

* Supports Linux only (Windows and macOS planned for future)
* Supports background applications that need to still run while the main window is not shown
* Supports multiple windows
* Per-window dependency injection scopes (overrides Blazor's per-component scoping model)
* Integrates with `Microsoft.Extensions.DependencyInjection`
* Integrates with `Microsoft.Extensions.Hosting`

## Getting started

The following guide explains the basics of creating a Testudo application.

### Creating the project

1. Create a new .NET console application project as a base.
2. Add a folder called `wwwroot` to the root of the project, this will house the web content.
3. Open the `.csproj` file for editing.
4. Replace the project SDK with `Microsoft.NET.Sdk.Razor` as this is needed for using Razor components.
5. Add a package reference to Testudo.
6. Set everything in the `wwwroot` folder as an embedded resource. Testudo delivers embedded files to the web view at runtime so the application can be compiled into a single file.

The final `.csproj` should look like this:

```xml


Exe
net8.0
enable
enable








```

### Setting up dependency injection

Testudo operates via `Microsoft.Extensions.DependencyInjection` and provides extension methods to help you easily add Testudo to your service container. Ensure you have a reference to the NuGet package if you don't already have it as a dependency in your project.

```xml




```

Now you can create the service container and add Testudo as follows. Logging has also been enabled via `Microsoft.Extensions.Logging` in this example for later, but this is not necessary for Testudo to function.

```csharp
var services = new ServiceCollection()
.AddLogging()
.AddTestudo();
```

Since Blazor creates a new scope for every Razor component, the default `IServiceProvider` implementation must be replaced with Testudo's implementation. With this implementation, scopes are created per-window so that components within the same window/webview can share state.

```csharp
var provider = new TestudoServiceProviderFactory()
.CreateServiceProvider(services);
```

If you are using `Microsoft.Extensions.Hosting`, you would add the `TestudoServiceProviderFactory` to your `IHostBuilder` instead.

```csharp
Host.CreateDefaultBuilder(args)
.UseServiceProviderFactory(new TestudoServiceProviderFactory())
.ConfigureServices((_, services) => services
.AddTestudo()
)
);
```

### Creating the Blazor application

Firstly, create a file in `wwwroot` named `index.html` and give it the standard Blazor markup.

```html



MyProject

Loading...

```

Note that `_framework/blazor.webview.js` is used here instead of the usual non-webview script.

Next, create an `_Imports.razor` file in the project root for global `.razor` using statements. Add the common Blazor includes here, and any others you like.

```csharp
@using Microsoft.AspNetCore.Components.Routing
@using Microsoft.AspNetCore.Components.Web
```

Create an `App.razor` file with the standard router setup.

```html





Not found

Sorry, there's nothing at this address.



```

Create a `MainLayout.razor` file. This will be a little different to the typical implementation, since Testudo isn't able to throw exceptions the usual way that Blazor does. You can use whatever method you like inside the `` component to handle the exception, but this example will use `Microsoft.Extensions.Logging` to log the exception to the logger configured in the service container, then display some text in the web view.

```html
@inject ILogger Logger


@Body


@{
Logger.LogError(exception, "An unhandled exception occurred in the Blazor application");

Unhandled exception! Please review the logs to see what went wrong.


}

```

Finally, you can create any pages you like. This example will just use a `HelloWorld.razor` component as follows.

```html
@page "/"

Hello World!


```

### Launching the application window

To show your Blazor application in a desktop window, simply resolve the `IWindowManager` service and call `OpenWindow`.

```csharp
var windowManager = provider.GetRequiredService();
windowManager.OpenWindow(new TestudoWindowConfiguration("/"));
```

Note how the type parameter given to `OpenWindow` is that of the Blazor application's root component. You can open a window with any component you like, but typically the main window would use something like this. Also note that the constructor parameter passed to `TestudoWindowConfiguration` matches the `@page` route declared in `HelloWorld.razor`. This tells the window to navigate to the `HelloWorld.razor` page when the window opens.

### Running the program

Finally, you can resolve the native application from the service container and call `Run` to begin the main program loop. The loop will terminate when the service container cleans up and disposes the service. You can also dispose it manually if you wish to terminate the application programmatically.

```csharp
var application = provider.GetRequiredService();
application.Run();
```

Beware that `ITestudoApplication.Run` will not return until the main program loop ends.

The final `Program.cs` may look something like this.

```csharp
// Configure the dependency injection container
var services = new ServiceCollection()
.AddLogging()
.AddTestudo();

// Create the dependency injection container
var provider = new TestudoServiceProviderFactory()
.CreateServiceProvider(services);

// Launch the main window
var windowManager = provider.GetRequiredService();
windowManager.OpenWindow(new TestudoWindowConfiguration("/"));

// Run the application
var application = provider.GetRequiredService();
application.Run();
```