Let's Create WPF Blazor Hybrid App

Introduction

Who would have thought there would come a day when we would all witness the blend of two of the best UI frameworks: WPF and Blazor? I've had the opportunity to work with both. Now, the dawn of this era is here, and it's time to seize the opportunity. you can now harness the power of both UI frameworks and create hybrid apps. An app that runs seamlessly on both desktop and web platforms. Well, folks, that day has finally arrived. Let's get cracking.

The category for the article is Blazor, but it could well have been WPF as well.

These are the steps you need to follow to create a Hybrid app.

  1. Create a WPF App
  2. Modify the project file
  3. Add Microsoft.AspNetCore.Components.WebView.Wpf namespaces
  4. Create wwwroot/Index.HTML file
  5. Add a new Razor Component
  6. Integrate a Razor component from the XAML Window

Step 1. Create a WPF App

Remember, we're developing a WPF app that will host a Blazor app within it. To begin, please create a new project in Visual Studio and search for 'WPF' then, select the highlighted option as shown in Image 1 below.

WPF App

Image 1. Create a WPF App

Now, proceed by naming your application. I've chosen the name 'WPFBlazorHybrid', after naming it, select its location and click 'Next'.

Next

Image 2. Configure your new project

Step 2. Modify the project file

With the WPF project set up, it's time to make some modifications to its project (.csproj) file. In the Solution Explorer, right-click on the project's name, 'WPFBlazorHybrid', and select "Edit Project File" to access the project file (WPFBlazorHybrid.csproj). Refer to Image 3 below to see how the project file might appear initially.

Project file

Image 3. Project file before making changes

We need to make a couple of changes here first.

  1. <Project Sdk> element: This specifies the SDK that the project uses. This SDK provides tools, compilers, and other necessary components for building the project. We need to change this from <Project Sdk="Microsoft.NET.Sdk"> to <Project Sdk="Microsoft.NET.Sdk.Razor">. With this change, the project will utilize razor syntax and features to create dynamic web content.
  2. <RootNamespace> in <PropertyGroup>: This is the root namespace for the project. We need to set that to WPFBlazorHybrid, after the change all code files in the project will belong to this namespace. This helps in avoiding naming conflicts between different namespaces.

After implementing these adjustments, your project file should look like Image 4.

Implementing

Image 4. Project file after making changes

Step 3. Add Microsoft.AspNetCore.Components.WebView.Wpf namespaces

Now, let's install the Microsoft.AspNetCore.Components.WebView.Wpf NuGet package to our application as shown in Image 5.

AspNetCore

Image 5. NuGet package: WebView

Step 4. Create a wwwroot/Index.HTML file

Every web app needs a wwwroot folder. Add one to your project and place the starting file, which will be Index.HTML, inside it. Below is the content of the Index.HTML.

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>WPFBlazorHybrid</title>
    <base href="/" />
</head>

<body>
    <div id="app">Loading...</div>
    <script src="_framework/blazor.webview.js"></script>
</body>

</html>

Code snippet 1. wwwroot/index.html

Step 5. Add a new Razor Component

Next, let's create a razor component. Add a component named 'QuickGridExample' to the root of the project. This component demonstrates a quick-grid demo.

To utilize QuickGrid, you'll need to install 'Microsoft.AspNetCore.Components.QuickGrid' from the NuGet package.

Below is the code for the 'QuickGridExample' component, which contains a list of programming languages displayed in a QuickGrid.

@using Microsoft.AspNetCore.Components.QuickGrid

<h1> Loading HTML in a WPF App: Not XAML </h1>
<h3>Programming Languages</h3>

<QuickGrid Items="@languages">
    <PropertyColumn Title="Name" Property="@(p => p.Name)" Sortable="true" />
    <PropertyColumn Title="Creator" Property="@(p => p.Creator)" Sortable="true" />
    <PropertyColumn Title="Year of Creation"  Property="@(p => p.YearOfCreation)" Format="yyyy" Sortable="true" />
</QuickGrid>

@code {
    record ProgrammingLanguage(string Name, string Creator, DateTime YearOfCreation);
    private IQueryable<ProgrammingLanguage> languages = new List<ProgrammingLanguage>
    {
        new ProgrammingLanguage("C", "Dennis Ritchie", new DateTime(1972, 1, 1)),
        new ProgrammingLanguage("C++", "Bjarne Stroustrup", new DateTime(1983, 1, 1)),
        new ProgrammingLanguage("Java", "James Gosling", new DateTime(1995, 1, 1)),
        new ProgrammingLanguage("Python", "Guido van Rossum", new DateTime(1991, 1, 1)),
        new ProgrammingLanguage("JavaScript", "Brendan Eich", new DateTime(1995, 1, 1)),
        new ProgrammingLanguage("C#", "Anders Hejlsberg", new DateTime(2000, 1, 1)),
        new ProgrammingLanguage("Swift", "Chris Lattner", new DateTime(2014, 1, 1)),
        new ProgrammingLanguage("Go", "Robert Griesemer, Rob Pike, Ken Thompson", new DateTime(2009, 1, 1)),
        new ProgrammingLanguage("Rust", "Graydon Hoare", new DateTime(2010, 1, 1)),
        new ProgrammingLanguage("Kotlin", "JetBrains", new DateTime(2011, 1, 1))
    }.AsQueryable();
}

Code snippet 2. QuickGridExample.razor

Your final folder structure should look like this.

 Folder structure

Image 6. Your project structure after following the above steps

Step 6. Integrate a Razor component from the XAML Window

Now, for the final step, let's map your WPF window with the Blazor component. Open your 'MainWindow.xaml' file and add the following namespace.

xmlns:blazor="clr-namespace:Microsoft.AspNetCore.Components.WebView.Wpf;assembly=Microsoft.AspNetCore.Components.WebView.Wpf"

Then, add the following markup.

 <blazor:BlazorWebView HostPage="wwwroot\index.html" Services="{DynamicResource services}">
     <blazor:BlazorWebView.RootComponents>
         <blazor:RootComponent Selector="#app" ComponentType="{x:Type local:QuickGridExample}" />
     </blazor:BlazorWebView.RootComponents>
 </blazor:BlazorWebView>

Code snippet 3. Modifications in MainWindow.Xaml

After making the above changes, your MainWindow.XAML would look like this.

<Window x:Class="WPFBlazorHybrid.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:blazor="clr-namespace:Microsoft.AspNetCore.Components.WebView.Wpf;assembly=Microsoft.AspNetCore.Components.WebView.Wpf"
        xmlns:local="clr-namespace:WPFBlazorHybrid"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Grid>
        <blazor:BlazorWebView HostPage="wwwroot\index.html" Services="{DynamicResource services}">
            <blazor:BlazorWebView.RootComponents>
                <blazor:RootComponent Selector="#app" ComponentType="{x:Type local:QuickGridExample}" />
            </blazor:BlazorWebView.RootComponents>
        </blazor:BlazorWebView>
    </Grid>
</Window>

Code snippet 4. MainWindow.Xaml

Open the code-behind file of MainWindow, which would be MainWindow.xaml.cs. Inside the MainWindow's constructor, right after calling the InitializeComponent() method, add the following code.

var serviceCollection = new ServiceCollection();
serviceCollection.AddWpfBlazorWebView();
Resources.Add("services", serviceCollection.BuildServiceProvider());

Code snippet 5. Modifications in MainWindow.Xaml. cs

Your final MainWindow.XAML.cs would look like this.

using System.Windows;
using Microsoft.Extensions.DependencyInjection;

namespace WPFBlazorHybrid
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            var serviceCollection = new ServiceCollection();
            serviceCollection.AddWpfBlazorWebView();
            Resources.Add("services", serviceCollection.BuildServiceProvider());
        }
    }
}

Code snippet 6. MainWindow.Xaml. cs

Alright, we are ready to launch this ship.

Hit 'run' and witness the magic. What you'll see is the MainWindow loading the <HTML> page right in front of your eyes.

HTML

Image 7. MainWindow loading HTML Markup

Let's make some cosmetic changes to enhance its UI. Add a <style> to your component.

<style>
    table {
        border-collapse: collapse;
        width: 100%;
        border: 2px solid #ccc;
    }

    th, td {
        padding: 12px;
        text-align: left;
        border-bottom: 1px solid #ddd;
    }

    h1, h3 {
        font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
        color: #333;
        text-align: center;
    }

    th {
        background-color: #28b463;
        color: white;
        font-weight: bold;
        font-size: 16px;
        border: none;
    }

    tr:nth-child(even) {
        background-color: #f2f2f2;
    }

    th:hover {
        background-color: #45a049;
    }
</style>

Code snippet 7. Style for QuickGridExample.razor

Now, run it again, and it should look as fine as the image below.

 Desktop app

Image 8. Desktop app loading web app with style

Conclusion

In the ever-evolving landscape of software development, the fusion of WPF and Blazor represents a significant stride forward. In this article, we've explored the integration process, from setting up the WPF project to incorporating Razor components and binding razor components with WPF. We learned how to render HTML content within the WPF application. With WPF and Blazor, developers can chart new territories in hybrid app development, This will shape the future of user experiences across diverse platforms.


Similar Articles