-
Notifications
You must be signed in to change notification settings - Fork 1.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Enhancement] (HybridWebView) - Invoke C# code from JavaScript #6446
Comments
I am almost there. The only issue is this one https://gitlab.com/fairking/mauiwebviewsample Also the Blazor is an interesting approach one: https://gitlab.com/fairking/mauiblazorwebview More info here: https://docs.microsoft.com/en-us/aspnet/core/blazor/javascript-interoperability/call-javascript-from-dotnet |
We have some ideas to make this type of IPC a lot easier: #1148 But it's not currently in the plans. We do have BlazorWebView, which enables running Razor components in .NET MAUI apps on all supported platforms, which includes C#/JS interop. |
|
But that doesn't cover the following use case: Hosting a Blazor Server app in a MAUI shell. Like that you can have an app that runs in a browser, with a MAUI version if you need enhanced support for local resources (e.g. local applications). In the end we just need a way to send messages from the Blazor Server app to the MAUI shell. At the moment this is not possible, correcct? |
@luetm not directly, but that's what this feature proposal would make easier. For now you could use a regular .NET MAUI WebView (not BlazorWebView) and register JavaScript callbacks to communicate between the code in the web view (which would be your Blazor Server app, which is really just a web app), and the .NET code running in .NET MAUI. But it's all manual. |
Yes, we really need a Blazor server app to access mobile resources through calling C# from JavaScript too. Is "Customizing a WebView" available in MAUI? |
This is already available in BlazorWebView. See https://gitlab.com/fairking/mauiblazorwebview/-/blob/master/MauiApp1/wwwroot/index.html#L43 |
@fairking Thanks for your reply. I will test it. By the way, is there any documentation about "DotNet" usage? Can I use HostPage with external url? |
|
@fairking It doesn't seem to resolve my problem. I need a WebView or BlazorWebView to connect remote Blazor server web site, and the JavaScript loaded from the web site needs to call C# functions to access device resource. I have tried setting remote url to HostPage of BlazorWebView without any RootCimponents. But the app shows "There is no content at". |
Sorry, I don't understand what you are trying to achieve. You say "Blazor server web site" which for me is simply a client running in BlazorWebView (using WebAssembly to compile) and communicating with the server via HTTP as any other SPA application. Unless I am wrong. Please someone correct me as I don't use Blazor. I prefer Vuejs/Typescript. |
@fairking Thanks for your help. I have a ASP. NET Core server implemented already, and I want to have a MAUI hyprid app to load web page on WebView from the server. On the web page, I need to use the Javascript to call client C# to access device resources, such as scanning QR Code or getting GPS. I can do this using Xamarin forms by customizing WebRender. |
I am not sure if you can communicate with Backend from ASP.NET renderer perspective (when you generate html), but you can definitely communicate with Backend from BlazorWebView client via javascript: <?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:b="clr-namespace:Microsoft.AspNetCore.Components.WebView.Maui;assembly=Microsoft.AspNetCore.Components.WebView.Maui"
xmlns:local="clr-namespace:MyProject"
x:Class="MyProject.MainPage"
BackgroundColor="{DynamicResource PageBackgroundColor}">
<b:BlazorWebView HostPage="wwwroot/index.html"></b:BlazorWebView>
</ContentPage>
<button onclick="callCsharp()" type="button">Click Me</button>
<script>
function callCsharp() {
window
.DotNet.invokeMethodAsync('MyProject', 'CallMeFromJs', "ABC")
.then((data) => { console.log(data); }); // It will print "ABC OK" in browser console or VS output window
}
</script>
using Microsoft.JSInterop;
namespace MyProject
{
public static class MauiProgram
{
public static MauiApp CreateMauiApp()
{
return MauiApp.CreateBuilder()
.UseMauiApp<App>()
.ConfigureFonts(fonts =>
{
fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
})
.Services.AddMauiBlazorWebView()
.Services.AddBlazorWebViewDeveloperTools()
.Build();
}
[JSInvokable]
public static async Task<object> CallMeFromJs(JsRequest request)
{
var result = (string)request + " OK";
return result;
}
}
} Note: Please see this sample: https://gitlab.com/fairking/mauiblazorwebview I hope it helps. |
@fairking Thanks for your sample. I need the HostPage of BlazorWebBrowser to be https://MyWebServer/, not local index.html file. This seems not supported in BlazorWebView. I can use WebView to load a remote url, but I can't find the way to call from Javascript to client C#. |
I am in a similar boat where I am using a webview around an existing angular app and need to have way for angular code call C# in .Net Maui. I had it working in Xamarin forms. |
It's a very interesting scenario but right now we don't have a specific plan for it. BTW even in BlazorWebView there is support for remote and local files, but it's still focused on using Blazor-based apps (for example, it wouldn't work well for React). |
@Eilon This is a long story. Long time ago I published a post on microsoft community forum about how cool would be to use MAUI as a cross platform framework to build Hybrid applications. The reason many people would love it because there is a huge market of SPA web applications (Angular/React/Vue) which companies and people want to bring into Desktop/Mobile/UWP world with a device API support (eg. Camera, Storage or Sqlite to be able to keep data offline). At the moment there is Electron and NativeScript but their backend is JS/TS, no C#/.NET support. So those developers can use C#/.NET as their backend. I started digging and found out it is not possible at the moment to move SPA app into MAUI project. First with WebView there is a problem with loading local files. Also it wasn't possible just to point to local index.html and have relative paths. It required to load index.html manually using FileStream and fix all relative paths of the js and css Regarding the BlazorWebView the thing with pointing to index.html and loading assets works as expected, but on the other hand there is a Blazor.start() which is not working properly on Android (windows is fine). I tried the latest release but looks like same issue. There is a hot reload thing which does not exist for hybrid applications in MAUI, but I have no problem with that. I develop and debug UI running the app in web browser ( If one of those WebViews could be fixed I may be able to start working on it, but if not then I will consider to try Tauri which is not mature yet and the Android support is in development but it looks very promising as a framework for hybrid multiplatform applications. Also I am waiting for NFC support for MAUI apps. But this is completely different story I assume. |
@Eilon How does BlazorWebView support remote files? It seems like using HostPage is not working for me. |
@fras2560 the HostPage has to be a local file, but you can reference scripts, images, etc. that are remote. |
WebView could have a interop way to communicate to javascript... Javascript -> C# and C# -> Javascript. Thats the biggest problem why we are using reactnative right now and not maui/.net for apps. |
We've moved this issue to the Backlog milestone. This means that it is not going to be worked on for the coming release. We will reassess the backlog following the current release and consider this item at that time. To learn more about our issue management process and to have better expectation regarding different types of issues you can read our Triage Process. |
So calling C# from javascript in .NET MAUI is not supported for .NET 7? |
There is a good tutorial of .NET Maui Blazor with JS calling .net https://www.youtube.com/watch?v=o7iVk5XFPd8&ab_channel=ProgressTelerik , this is a .NET MAUI Blazor loading an Angular project and calling C# functions in the BLazor project from that angular Project |
I guess we are stuck with using navigation events / urls, which is slow unfortunately and limited on data that can be passed |
I found a solution that works, to be fair this is in the GitHub example that is mentioned above but at first, I thought it didn't work till I realised you have to make sure that you get the project reference name correct: So if you use:
You DO NOT need to register this in the @code or codebase e.g. in the "OnInitializedAsync() or OnAfterRenderAsync(bool firstRender)" All you need to do is add it to a js file that is referenced by index.html or directly to that page header, as a JS function that you call at some point. The important part of this is 'MyMauiApp' this MUST be the NAME of the project NOT the default namespace that's created from that name. For example, if the project was called "FOO-BAR" the default namespace would be called "Foo_Bar" but the application name would be "FOO-BAR" and that's what you would need to use, in the js function to call c# Usually, the two are the same unless you use crazy naming conventions like I tend to. This will the C# function CallMeFromJs and pass the pram 'SomeString' :) The CS is as normal then
This works as per usual with Blazor but inside Maui and allows you to call a C# function on a razor page from JS. This will work for Android and Windows as I have tested and prove that locally. I have not yet been able to test this on ISO or Mac as I need to set up a mac VM and iPhone emulator first but I suspect it will do. It seems the key is using "DotNet.invokeMethodAsync" as opposed to registering a js function and passing a reference of the project to it like you might normally do: I hope this helps others |
Does the above pattern work outside of Blazor? |
yes it does, so long as the child project is within the blazor project it seems, if you watch that Microsoft youtube video I posted above they do exactly that, they load an angular projected into blazor inside of net MAUI and are able to call C# functions in blazor from angular |
Thanks. Can't use blazor for my particular Maui app, so will continue with the url navigation hook pattern. |
@PTAHume are you saying if I am using a webview in a dotnet maui project I can call
|
Tried it, does not work, I think you have to have Blazor |
MAUI would literally be the biggest thing if MSFT took this seriously. A multiplat, HTML-UI frontend w access to native APIs? Electron replacement yes? I'm basically on the edge of my seat in anticipation waiting to see if they actually make this robust. Don't drop the ball, please. There's a bright future of MAUI JavaScript games, HTML/CSS UIs, and all multiplat, if you simply make C# and JS interop work. |
For now navigation event hijacking is working well if a bit hacky. I even have cases where the c# code immediately then calls a web browser JavaScript function in response. Not the cleanest code but does work just fine as a bidirectional channel of calls. |
Hi everyone, I started an experimental control that you can check out here: #12009 Please let me know your feedback! |
Here is a little sample project providing a workaround for this gap: |
Thats the simply solution: |
@mahop-net Hello, your sample project is very helpful for me. Thank you so much! |
Should just send a command name and an object like WKWebView? The actual running code should still be completed by C# itself, which is the safest. https://developer.apple.com/documentation/webkit/wkscriptmessagehandler |
Think my PR adds that sort of functionality Eilon/MauiHybridWebView#1 |
This works with a BlazorWebView Control, tested on android with .net8 preview 6, with no additional library. public class ZoeyLiteWebView : BlazorWebView
{
WebViewJavaScriptInterop jsInterop;
public ZoeyLiteWebView(WebViewJavaScriptInterop jsInterop)
{
this.jsInterop = jsInterop;
}
protected override void OnHandlerChanged()
{
jsInterop.Bind(this);
base.OnHandlerChanged();
}
}
public class WebViewJavaScriptInterop : Java.Lang.Object
{
public void Bind(ZoeyLiteWebView webView)
{
var nativeWebView = ((WebView?)webView.ToPlatform(webView.Handler.MauiContext)) ?? throw new InvalidOperationException("Cannot get platform webview");
nativeWebView.AddJavascriptInterface(this, "native");
}
[JavascriptInterface]
[Export("invoke")]
public void Invoke(string info)
{
}
}
|
@sake402 thanks, your solution worked great for us with a normal WebView using .NET 7 as we only needed Android support. We are migrating from ReactNative to .NET MAUI and the JavaScript to C# interop was one of the issues. We load an external website into the WebView and didn't want to have to modify the external website, so we are mimicking the ability for the website to continue to invoke The code below is working for us: using Microsoft.Maui.Platform;
namespace MyMauiApp.Pages;
public class HomePage : ContentPage
{
private readonly HybridWebView _webView;
public HomePage()
{
_webView = new HybridWebView(new HybridWebViewJavaScriptInterop());
_webView.HorizontalOptions = LayoutOptions.Fill;
_webView.VerticalOptions = LayoutOptions.Fill;
_webView.Loaded += OnWebViewOnLoaded;
_webView.Source = "https://google.com/";
Content = _webView;
}
private void OnWebViewOnLoaded(object sender, EventArgs e)
{
}
}
public class HybridWebView : Microsoft.Maui.Controls.WebView
{
private readonly HybridWebViewJavaScriptInterop _jsInterop;
public HybridWebView(HybridWebViewJavaScriptInterop jsInterop)
{
_jsInterop = jsInterop;
}
protected override void OnHandlerChanged()
{
_jsInterop.Bind(this);
base.OnHandlerChanged();
}
public void OnMessageReceived(string message)
{
Dispatcher.Dispatch(async () =>
{
var toast = CommunityToolkit.Maui.Alerts.Toast.Make($"JavaScript Message:\n{message}");
await toast.Show();
});
}
}
public class HybridWebViewJavaScriptInterop : Java.Lang.Object
{
private HybridWebView _webView;
public void Bind(HybridWebView webView)
{
_webView = webView;
if (_webView.Handler?.MauiContext != null)
{
var nativeWebView = (Android.Webkit.WebView)_webView.ToPlatform(_webView.Handler.MauiContext);
nativeWebView.AddJavascriptInterface(this, "ReactNativeWebView"); // Change this to whatever you want
}
}
[Android.Webkit.JavascriptInterface]
[Java.Interop.Export("postMessage")]
public void PostMessage(string message)
{
_webView?.OnMessageReceived(message);
}
} And we added this to this code snippet to the MainActivity.cs // Enable remote debugging Android WebViews using Edge or Chrome
if (Android.OS.Build.VERSION.SdkInt >= Android.OS.BuildVersionCodes.Kitkat)
{
Android.Webkit.WebView.SetWebContentsDebuggingEnabled(true);
} Then we inspected and debugged the WebView using Edge and was able to successfully send this message to C#: window.ReactNativeWebView.postMessage('Hello, World!') |
Do you have a chance to try on iOS? Thanks a lot! |
This is very important! |
I have worked on a HybridWebView control based on the work by @Eilon and modified it to support loading a public website and support calling C# code from JS. It uses IWKScriptMessageHandlerWithReply on iOS and ShouldInterceptRequest on Android to provide an async pattern returning promises in Javascript. As it should work as a regular browser it is based on the regular WebView. The code is a bit messy. Just needed it for a POC. Would it be interesting to look at? |
Alrighty, I think that all the key parts of HybridWebView are committed into the .NET MAUI 9 release! As of #25077, you can have JavaScript code invoke .NET methods and get results back as well. So, I'm closing this issue. Please try it out upon release and file new issues for any issues you might have. Thank you! |
Description
In Xamarin.Forms to invoke C# code from JavaScript in a WebView there is official documentation from Microsoft:
https://docs.microsoft.com/en-us/xamarin/xamarin-forms/app-fundamentals/custom-renderer/hybridwebview
This suggestion is to add new documentation on HybridWebView for .NET Maui as custom renderers are used in the Xamarin.Forms documentation and now with .NET Maui custom renderers are deprecated so it would be interesting to add examples using Handlers and in the Xamarin.Forms the Windows sample supports UWP but now with .NET Maui we need a WinUI 3 compatible sample, and Microsoft now officially supports macOS, so an example should be added for this platform as well.
Public API Changes
Create HybridWebView for .NET Maui using Handlers instead of custom renderers and adding examples for WinUI 3 and macOS.
Intended Use-Case
Invoking C# code from JavaScript in a WebView is very common in many applications and Xamarin.Forms has documentation from Microsoft covering this topic so it stands to reason that .NET Maui which is the successor technology to Xamarin.Forms will receive the same support in all officially supported platforms: Android, iOS, WinUI 3 and macOS.
The text was updated successfully, but these errors were encountered: