Blazor on desktop is one of latest hot topics and .NET Conf: Focus on Blazor only added more fuel to fire. Blazor seems to come everywhere and it’s unstoppable. One of interesting desktop experiments is WebWindow by Steve Sanderson. It’s cross-platform component to make Blazor WebAssembly applications run on desktop. Let’s take a closer look at WebWindow and Blazor on desktop.
Warning! WebWindow is experimental project by Steve Sanderson – one of masterminds behind Blazor. There are no ready-made stable packages or components, no beautiful templates and other luxury like this. Get ready to make hands really dirty with raw bits and bytes!
Why Blazor on desktop?
Blazor WebAssembly needs only rendering engine with WebAssembly support to run and most of modern browsers support WebAssembly. There Blazor applications doesn’t necessarily need web server to run. Web server is just one option to deliver Blazor application so browser can download and run it.
If web server is not necessary then there’s question to ask: can we somehow remove web server from equation and make those Blazor WebAssembly apps look and feel more like desktop application? Blazor on desktop is here to address this question with high chances to answer “yes”.
What is WebWindow?
WebWindow is .NET Core library that makes it possible to run HTML, JavaScript and WebAssembly applications in local browser window. It uses some existing browser or web renderer library to create a window where web application is loaded and run.
WebWindow is cross-platfrom library. Current experimental implementation of WebWindow works on following platforms:
- Windows – needs Chromium-based Edge
- Linux – uses WebKit
- Mac – needs Safari
Here’s how we start Blazor on desktop on all three platforms.
using WebWindows.Blazor; namespace BlazorDesktopApp
{ public class Program { static void Main(string[] args) { ComponentsDesktop.Run<Startup>("My Blazor App", "wwwroot/index.html"); } } }
WebWindow is made available on same desktop platforms where .NET Core runs.
Downloading and running WebWindow examples
Experimental WebWindow code lives in GitHub repository SteveSandersonMS/WebWindow.
- Clone or download latest WebWindows version from master branch
- Run Visual Studio 2019 and update it to latest version
- Open WebWindow.Samples.sln file in Visual Studio
There are three projects in samples solution:
- HelloWorldApp – simple Hello, World! demonstrating basics of WebWindow
- VuewFileExplorer – sample of JavaScript based local file browser
- BlazorDesktopApp – running Blazor WebAssembly app on desktop
Let’s see what’s inside those sample applications.
Hello WebView
First project to explore is Hello, world! It is not about Blazor but WebWindow component that hosts HTML and Blazor applications. This is console application that creates new WebWindow and shows local index.html file.
using WebWindows; namespace HelloWorldApp { class Program { static void Main(string[] args) { var window = new WebWindow("My first WebWindow app"); window.NavigateToLocalFile("wwwroot/index.html"); window.WaitForExit(); } } }
WaitForExit() method of WebWindow stops execution of Main() method until windows is closed.
When we run application then it opens in local web view window. The following screenshot shows Hello, World application running on Windows 10.
With this example our first step is made.
Browsing file system of host machine
Next WebWindow example is in project called VueFileExplorer. It makes step further with WebWindow and introduces communication between user interface shown in web view and .NET Core process hosting it. WebWindow defines OnWebMessageReceived event handler and SendMessage command. These two members of WebWindow class enable two-way communication between web view and hosting process.
VueFileExplorer uses Vue.js to build two-pane window to explore file system. Left pane is for files and folders, right pane is for file content like shown on the following screenshot.
When file or folder is clicked then JavaScript sends message to window. This is the Vue app serving user interface and communicating with host application.
var app = new Vue({ el: '#app', data: { directoryInfo: null, fileInfo: null }, methods: { navigateTo: function (relativePath, event) { event.preventDefault(); window.external.sendMessage(JSON.stringify({ command: 'navigateTo', basePath: app.directoryInfo.name, relativePath: relativePath })); }, showFile: function (fullName, event) { event.preventDefault(); window.external.sendMessage(JSON.stringify({ command: 'showFile', fullName: fullName })) ; } } }); window.external.receiveMessage(function (messageJson) { var message = JSON.parse(messageJson); switch (message.command) { case 'showDirectory': app.directoryInfo = message.arg; break; case 'showFile': app.fileInfo = message.arg; break; } }); window.external.sendMessage(JSON.stringify({ command: 'ready' }));
Here’s the fragment of code from Program class demonstrating how WebWindow messages are listened and processed.
static void Main(string[] args) { var window = new WebWindow(".NET Core + Vue.js file explorer"); window.OnWebMessageReceived += HandleWebMessageReceived; window.NavigateToLocalFile("wwwroot/index.html"); window.WaitForExit(); } static void HandleWebMessageReceived(object sender, string message) { var window = (WebWindow)sender; var parsedMessage = JsonDocument.Parse(message).RootElement; switch (parsedMessage.GetProperty("command").GetString()) { case "ready": ShowDirectoryInfo(window, Directory.GetCurrentDirectory()); break; case "navigateTo": var basePath = parsedMessage.GetProperty("basePath").GetString(); var relativePath = parsedMessage.GetProperty("relativePath").GetString(); var destinationPath = Path.GetFullPath(Path.Combine(basePath, relativePath)) .TrimEnd(Path.DirectorySeparatorChar); ShowDirectoryInfo(window, destinationPath); break; case "showFile": var fullName = parsedMessage.GetProperty("fullName").GetString(); ShowFileContents(window, fullName); break; } }
Based on message sent from web view the response is built and sent to web view as message. Here’s how showing of file contents is implemented.
private static void ShowFileContents(WebWindow window, string fullName) { var fileInfo = new FileInfo(fullName); SendCommand(window, "showFile", null); // Clear the old display first SendCommand(window, "showFile", new { name = fileInfo.Name, size = fileInfo.Length, fullName = fileInfo.FullName, text = ReadTextFile(fullName, maxChars: 100000), }); }
SendCommand is simple static method that sends prepared command to web view.
static void SendCommand(WebWindow window, string commandName, object arg) { window.SendMessage(JsonSerializer.Serialize(new { command = commandName, arg = arg })); }
As we can see then we can use WebWindow to host JavaScript application and make it use host system through messaging.
Blazor desktop application
The pearl of WebWindow example applications is the one that hosts Blazor WebAssembly. The example uses default Blazor WebAssembly application with famous Counter and Fetch data views. Here is the example of weather forecasts we have seen in numerous Blazor demos. Now it is running on your desktop instead of web server.
Where comes the data to weather forecast view if Blazor application is hosted in web window and there’s no web server running? It’s coming straight from disk. Here’s the FetchData page from Blazor desktop application project.
@page "/fetchdata" @using System.IO @using System.Text.Json <h1>Weather forecast</h1> <p>This component demonstrates fetching data from the server.</p> @if (forecasts == null) { <p><em>Loading...</em></p> } else { <table class="table"> <thead> <tr> <th>Date</th> <th>Temp. (C)</th> <th>Temp. (F)</th> <th>Summary</th> </tr> </thead> <tbody> @foreach (var forecast in forecasts) { <tr> <td>@forecast.Date.ToShortDateString()</td> <td>@forecast.TemperatureC</td> <td>@forecast.TemperatureF</td> <td>@forecast.Summary</td> </tr> } </tbody> </table> } @code { WeatherForecast[] forecasts; protected override async Task OnInitializedAsync() { var forecastsJson = await File.ReadAllTextAsync("wwwroot/sample-data/weather.json"); forecasts = JsonSerializer.Deserialize<WeatherForecast[]>(forecastsJson, new JsonSerializerOptions { PropertyNameCaseInsensitive = true }); } public class WeatherForecast { public DateTime Date { get; set; } public int TemperatureC { get; set; } public string Summary { get; set; } public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); } }
Instead of using HttpClient like examples of server hosted Blazor do, here they are accessing file system to load and deserialize weather forecast data.
In all other means it is the same Blazor application we get when we create new Blazor application. But this time it runs on desktop.
Wrapping up
Blazor WebApplication (formerly known as client-side Blazor) is going through revolutionary times. There are many things happening around it and making Blazor run on desktop is perhaps the most interesting part. Although WebWindow is in early experimental stages and it’s not even clear if this approach will stay here long enough to get to stable version, it is still very promising. WebWindow and desktop support are in my opinion important part of Blazor-everywhere vision.
View Comments (5)
Really hope WebWindow becomes a thing and offers some native OS InterOp. It would make for some nice cross-platform desktop apps that don’t rely heavily on the native OS. Exciting stuff!!
I hope they build something for mobile phones too. Something that's same easy to wire to my experimental solution.
Is there any development on this? I fully understand experimental nature of this, but are we able to sure what is the future of WebWindow today?
I have no information about future of WebWindow. There have been some new Blazor WebAssembly versions but it seems like there will be big news when Blazor WebAssembly is officially launched at May this year. Until this I don't recommend to put any cards on WebWindow.
Hi , so I am thinking of learning a new framework and I was hesitating between Blazor or flutter. I like blazor but aside from writting code in c# which I already know (I don't mind learning dart tho) what will set Blazor apart from flutter, I think the fact Blazor will be working as webassembly is nice but I see a lot of hype around flutter too can you give me any insights on why to choose Blazor over flutter?