Skip to content

Commit

Permalink
Add support for JS root components in BlazorWebView (#2293)
Browse files Browse the repository at this point in the history
* Works in .NET MAUI, WPF, and WinForms
  • Loading branch information
Eilon committed Sep 1, 2021
1 parent 5ea2a73 commit 83a4d0b
Show file tree
Hide file tree
Showing 23 changed files with 300 additions and 82 deletions.
44 changes: 24 additions & 20 deletions src/BlazorWebView/samples/BlazorWinFormsApp/Form1.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,34 +3,38 @@

using System;
using System.Windows.Forms;
using BlazorWinFormsApp.Pages;
using Microsoft.AspNetCore.Components.Web;
using Microsoft.AspNetCore.Components.WebView.WindowsForms;
using Microsoft.Extensions.DependencyInjection;
using WebViewAppShared;

namespace BlazorWinFormsApp
{
public partial class Form1 : Form
{
private readonly AppState _appState = new();
public partial class Form1 : Form
{
private readonly AppState _appState = new();

public Form1()
{
var serviceCollection = new ServiceCollection();
serviceCollection.AddBlazorWebView();
serviceCollection.AddSingleton<AppState>(_appState);
InitializeComponent();
public Form1()
{
var serviceCollection = new ServiceCollection();
serviceCollection.AddBlazorWebView();
serviceCollection.AddSingleton<AppState>(_appState);
InitializeComponent();

blazorWebView1.HostPage = @"wwwroot\index.html";
blazorWebView1.Services = serviceCollection.BuildServiceProvider();
blazorWebView1.RootComponents.Add<Main>("#app");
}
blazorWebView1.HostPage = @"wwwroot\index.html";
blazorWebView1.Services = serviceCollection.BuildServiceProvider();
blazorWebView1.RootComponents.Add<Main>("#app");
blazorWebView1.RootComponents.RegisterForJavaScript<MyDynamicComponent>("my-dynamic-root-component");
}

private void button1_Click(object sender, EventArgs e)
{
MessageBox.Show(
owner: this,
text: $"Current counter value is: {_appState.Counter}",
caption: "Counter");
}
private void button1_Click(object sender, EventArgs e)
{
MessageBox.Show(
owner: this,
text: $"Current counter value is: {_appState.Counter}",
caption: "Counter");
}

private void _webViewActionButton_Click(object sender, EventArgs e)
{
Expand Down
24 changes: 24 additions & 0 deletions src/BlazorWebView/samples/BlazorWinFormsApp/wwwroot/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,27 @@
<base href="/" />
<link href="css/app.css" rel="stylesheet" />
<link href="BlazorWinFormsApp.styles.css" rel="stylesheet" />
<script>
const addedComponents = [];
let numAddedComponents = 0;

var i = 1;
async function addDynamicComponent() {
let containerElement = document.createElement('div');
containerElement.id = `root-container-${++numAddedComponents}`;
document.body.appendChild(containerElement);
const component = await Blazor.rootComponents.add(containerElement, 'my-dynamic-root-component', { incrementAmount: i });
addedComponents.push({ component, containerElement });

i++;
}

function removeRootComponent() {
// Treat it like a FIFO queue
const { component, containerElement } = addedComponents.shift();
component.dispose();
}
</script>
</head>

<body>
Expand All @@ -19,6 +40,9 @@
<a class="dismiss">🗙</a>
</div>

<button onclick="addDynamicComponent()">Add dynamic component</button>
<button onclick="removeRootComponent()">Remove root component</button>

<script src="_framework/blazor.webview.js"></script>
</body>

Expand Down
8 changes: 6 additions & 2 deletions src/BlazorWebView/samples/BlazorWpfApp/MainWindow.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System.Windows;
using Microsoft.AspNetCore.Components.Web;
using Microsoft.Extensions.DependencyInjection;
using WebViewAppShared;

namespace BlazorWpfApp
{
Expand All @@ -21,9 +23,11 @@ public MainWindow()
Resources.Add("services", serviceCollection.BuildServiceProvider());

InitializeComponent();
}

private void Button_Click(object sender, RoutedEventArgs e)
blazorWebView1.RootComponents.RegisterForJavaScript<MyDynamicComponent>("my-dynamic-root-component");
}

private void Button_Click(object sender, RoutedEventArgs e)
{
MessageBox.Show(
owner: this,
Expand Down
24 changes: 24 additions & 0 deletions src/BlazorWebView/samples/BlazorWpfApp/wwwroot/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,27 @@
<base href="/" />
<link href="css/app.css" rel="stylesheet" />
<link href="BlazorWpfApp.styles.css" rel="stylesheet" />
<script>
const addedComponents = [];
let numAddedComponents = 0;

var i = 1;
async function addDynamicComponent() {
let containerElement = document.createElement('div');
containerElement.id = `root-container-${++numAddedComponents}`;
document.body.appendChild(containerElement);
const component = await Blazor.rootComponents.add(containerElement, 'my-dynamic-root-component', { incrementAmount: i });
addedComponents.push({ component, containerElement });

i++;
}

function removeRootComponent() {
// Treat it like a FIFO queue
const { component, containerElement } = addedComponents.shift();
component.dispose();
}
</script>
</head>

<body>
Expand All @@ -19,6 +40,9 @@
<a class="dismiss">🗙</a>
</div>

<button onclick="addDynamicComponent()">Add dynamic component</button>
<button onclick="removeRootComponent()">Remove root component</button>

<script src="_framework/blazor.webview.js"></script>
</body>

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
@implements IAsyncDisposable

<div class="my-dynamic-root-component">
<p>
This is a Blazor component.
</p>
<p>
Click count: <strong class="click-count">@clicks</strong>
(will increment in steps of <strong class="increment-amount-value">@IncrementAmount</strong>)
</p>

<button class="increment" @onclick="Increment">Increment</button>

</div>

@code {
[Parameter] public int IncrementAmount { get; set; } = 7;

int clicks;

void Increment()
{
clicks += IncrementAmount;
}

public async ValueTask DisposeAsync()
{
await Task.Delay(1000);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
.my-dynamic-root-component {
border: 2px dashed blue;
padding: 1em;
margin: 1em 0;
}
Original file line number Diff line number Diff line change
Expand Up @@ -95,8 +95,8 @@ private void StartWebViewCoreIfPossible()

var mauiAssetFileProvider = new AndroidMauiAssetFileProvider(Context.Assets, contentRootDir);

var jsComponents = new JSComponentConfigurationStore();
_webviewManager = new AndroidWebKitWebViewManager(this, NativeView, Services!, MauiDispatcher.Instance, mauiAssetFileProvider, jsComponents, hostPageRelativePath);
_webviewManager = new AndroidWebKitWebViewManager(this, NativeView, Services!, MauiDispatcher.Instance, mauiAssetFileProvider, VirtualView.JSComponents, hostPageRelativePath);

if (RootComponents != null)
{
foreach (var rootComponent in RootComponents)
Expand Down
14 changes: 11 additions & 3 deletions src/BlazorWebView/src/Maui/BlazorWebView.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,20 @@
using System;
using System.Collections.ObjectModel;
using Microsoft.AspNetCore.Components.Web;

namespace Microsoft.AspNetCore.Components.WebView.Maui
{
public class BlazorWebView : Microsoft.Maui.Controls.View, IBlazorWebView
{
private readonly JSComponentConfigurationStore _jSComponents = new();

public BlazorWebView()
{
RootComponents = new RootComponentsCollection(_jSComponents);
}

JSComponentConfigurationStore IBlazorWebView.JSComponents => _jSComponents;

public string? HostPage { get; set; }

public ObservableCollection<RootComponent> RootComponents { get; } = new();
public RootComponentsCollection RootComponents { get; }
}
}
7 changes: 3 additions & 4 deletions src/BlazorWebView/src/Maui/BlazorWebViewHandler.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using System.Collections.ObjectModel;
using System.Linq;
using System.Linq;
using Microsoft.Maui;
using Microsoft.Maui.Handlers;

Expand Down Expand Up @@ -40,8 +39,8 @@ public static void MapRootComponents(BlazorWebViewHandler handler, IBlazorWebVie
#if !NETSTANDARD
private string? HostPage { get; set; }

private ObservableCollection<RootComponent>? _rootComponents;
private ObservableCollection<RootComponent>? RootComponents
private RootComponentsCollection? _rootComponents;
private RootComponentsCollection? RootComponents
{
get => _rootComponents;
set
Expand Down
6 changes: 3 additions & 3 deletions src/BlazorWebView/src/Maui/IBlazorWebView.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
using System;
using System.Collections.ObjectModel;
using Microsoft.AspNetCore.Components.Web;
using Microsoft.Maui;

namespace Microsoft.AspNetCore.Components.WebView.Maui
{
public interface IBlazorWebView : IView
{
string? HostPage { get; set; }
ObservableCollection<RootComponent> RootComponents { get; }
RootComponentsCollection RootComponents { get; }
JSComponentConfigurationStore JSComponents { get; }
}
}
17 changes: 17 additions & 0 deletions src/BlazorWebView/src/Maui/RootComponentsCollection.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
using System.Collections.ObjectModel;
using Microsoft.AspNetCore.Components.Web;

namespace Microsoft.AspNetCore.Components.WebView.Maui
{
public class RootComponentsCollection : ObservableCollection<RootComponent>, IJSComponentConfiguration
{
private readonly JSComponentConfigurationStore _jSComponents;

public RootComponentsCollection(JSComponentConfigurationStore jSComponents)
{
_jSComponents = jSComponents;
}

public JSComponentConfigurationStore JSComponents => _jSComponents;
}
}
12 changes: 12 additions & 0 deletions src/BlazorWebView/src/Maui/WebViewManagerCreatedEventArgs.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
namespace Microsoft.AspNetCore.Components.WebView.Maui
{
public class WebViewManagerCreatedEventArgs
{
public WebViewManagerCreatedEventArgs(WebViewManager webViewManager)
{
WebViewManager = webViewManager;
}

public WebViewManager WebViewManager { get; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,8 @@ private void StartWebViewCoreIfPossible()
// WinUIWebViewManager so that loading static assets is done entirely there.
var mauiAssetFileProvider = new NullFileProvider();

var jsComponents = new JSComponentConfigurationStore();
_webviewManager = new WinUIWebViewManager(NativeView, new WinUIWebView2Wrapper(NativeView), Services!, MauiDispatcher.Instance, mauiAssetFileProvider, jsComponents, hostPageRelativePath, contentRootDir);
_webviewManager = new WinUIWebViewManager(NativeView, new WinUIWebView2Wrapper(NativeView), Services!, MauiDispatcher.Instance, mauiAssetFileProvider, VirtualView.JSComponents, hostPageRelativePath, contentRootDir);

if (RootComponents != null)
{
foreach (var rootComponent in RootComponents)
Expand Down
4 changes: 2 additions & 2 deletions src/BlazorWebView/src/Maui/iOS/BlazorWebViewHandler.iOS.cs
Original file line number Diff line number Diff line change
Expand Up @@ -115,8 +115,8 @@ private void StartWebViewCoreIfPossible()

var mauiAssetFileProvider = new iOSMauiAssetFileProvider(contentRootDir);

var jsComponents = new JSComponentConfigurationStore();
_webviewManager = new IOSWebViewManager(this, NativeView, Services!, MauiDispatcher.Instance, mauiAssetFileProvider, jsComponents, hostPageRelativePath);
_webviewManager = new IOSWebViewManager(this, NativeView, Services!, MauiDispatcher.Instance, mauiAssetFileProvider, VirtualView.JSComponents, hostPageRelativePath);

if (RootComponents != null)
{
foreach (var rootComponent in RootComponents)
Expand Down

0 comments on commit 83a4d0b

Please sign in to comment.