CallMeFromScriptReturn(string message, int value, int? optional = 0)
+ {
+ _mainPage.WriteToLog($"I'm a .NET method called from JavaScript with message='{message}' and value={value} and optional={optional}");
+
+ await Task.Delay(50);
+
+ return value;
+ }
}
private enum HybridAppPageID
diff --git a/MauiCSharpInteropWebView/Resources/Raw/hybrid_root/js/HybridWebView.js b/MauiCSharpInteropWebView/Resources/Raw/hybrid_root/js/HybridWebView.js
index 836106a..59ef490 100644
--- a/MauiCSharpInteropWebView/Resources/Raw/hybrid_root/js/HybridWebView.js
+++ b/MauiCSharpInteropWebView/Resources/Raw/hybrid_root/js/HybridWebView.js
@@ -1,32 +1,39 @@
// Standard methods for HybridWebView
-window.HybridWebView = {
- "SendRawMessageToDotNet": function SendRawMessageToDotNet(message) {
- window.HybridWebView.SendMessageToDotNet(0, message);
- },
-
- "SendInvokeMessageToDotNet": function SendInvokeMessageToDotNet(methodName, paramValues) {
- if (typeof paramValues !== 'undefined') {
- if (!Array.isArray(paramValues)) {
- paramValues = [paramValues];
- }
- for (var i = 0; i < paramValues.length; i++) {
- paramValues[i] = JSON.stringify(paramValues[i]);
- }
+class HybridWebViewDotNetHost {
+
+ constructor() {
+ this._nextCallbackId = 1;
+ this._callbackMap = new Map();
+ }
+
+ // Methods
+ SendRawMessageToDotNet(message) {
+ this.SendMessageToDotNet(0, message);
+ }
+
+ SendInvokeMessageToDotNet(className, methodName, paramValues = []) {
+ if (paramValues && !Array.isArray(paramValues)) {
+ paramValues = Array.of(paramValues);
}
- window.HybridWebView.SendMessageToDotNet(1, JSON.stringify({ "MethodName": methodName, "ParamValues": paramValues }));
- },
+ let params = paramValues.map(x => JSON.stringify(x));
+
+ let callback = this.CreateCallback();
- "SendMessageToDotNet": function SendMessageToDotNet(messageType, messageContent) {
+ this.SendMessageToDotNet(1, JSON.stringify({ "ClassName": className, "MethodName": methodName, "CallbackId": callback.id, "ParamValues": params }));
+
+ return callback.promise;
+ }
+
+ SendMessageToDotNet(messageType, messageContent) {
var message = JSON.stringify({ "MessageType": messageType, "MessageContent": messageContent });
if (window.chrome && window.chrome.webview) {
// Windows WebView2
window.chrome.webview.postMessage(message);
}
- else if (window.webkit && window.webkit.messageHandlers && window.webkit.messageHandlers.webwindowinterop)
- {
+ else if (window.webkit && window.webkit.messageHandlers && window.webkit.messageHandlers.webwindowinterop) {
// iOS and MacCatalyst WKWebView
window.webkit.messageHandlers.webwindowinterop.postMessage(message);
}
@@ -35,4 +42,51 @@ window.HybridWebView = {
hybridWebViewHost.sendMessage(message);
}
}
-};
+
+ CreateProxy(className) {
+ const self = this;
+ const proxy = new Proxy({}, {
+ get: function (target, methodName) {
+ return function (...args) {
+ return self.SendInvokeMessageToDotNet(className, methodName, args);
+ }
+ }
+ });
+
+ return proxy;
+ }
+
+ ResolveCallback(id, message) {
+ const callback = this._callbackMap.get(id);
+
+ if (callback) {
+ const obj = JSON.parse(message);
+ callback.resolve(obj);
+ }
+ }
+
+ RejectCallback(id, message) {
+ const callback = this._callbackMap.get(id);
+
+ if (callback) {
+ callback.resolve(message);
+ }
+ }
+
+ CreateCallback() {
+ let callback = {};
+
+ callback.id = this._nextCallbackId++;
+ callback.promise = new Promise((resolve, reject) => {
+ callback.resolve = resolve;
+ callback.reject = reject;
+ });;
+
+ this._callbackMap.set(callback.id, callback);
+
+ return callback;
+ }
+}
+
+HybridWebViewDotNetHost.Current = new HybridWebViewDotNetHost();
+window.HybridWebView = HybridWebViewDotNetHost.Current;
\ No newline at end of file
diff --git a/MauiCSharpInteropWebView/Resources/Raw/hybrid_root/methodinvoke.html b/MauiCSharpInteropWebView/Resources/Raw/hybrid_root/methodinvoke.html
index 26d0013..5a5de8d 100644
--- a/MauiCSharpInteropWebView/Resources/Raw/hybrid_root/methodinvoke.html
+++ b/MauiCSharpInteropWebView/Resources/Raw/hybrid_root/methodinvoke.html
@@ -11,10 +11,20 @@
return sum;
}
- function CallDotNetMethod() {
+ async function CallDotNetMethod() {
Log('Calling a method in .NET with some parameters');
- HybridWebView.SendInvokeMessageToDotNet("CallMeFromScript", ["msg from js", 987]);
+ try {
+ HybridWebView.SendInvokeMessageToDotNet("host", "CallMeFromScript", ["msg from js", 987]);
+
+ const hostObj = HybridWebView.CreateProxy("host");
+ const result = await hostObj.CallMeFromScriptReturn("Hello", 42);
+
+ Log("Method call Result was " + result);
+ }
+ catch (ex) {
+ Log(ex);
+ }
}
@@ -30,8 +40,19 @@ HybridWebView demo: Method invoke
Methods can be invoked in both directions:
- - JavaScript can invoke .NET methods by calling
HybridWebView.SendInvokeMessageToDotNet("DotNetMethodName", ["param1", 123]);
.
- .NET can invoke JavaScript methods by calling
var sum = await webView.InvokeJsMethodAsync("JsAddNumbers", 123, 456);
.
+ -
+ JavaScript can invoke .NET methods by calling
+
+ // Call the method directly
+ HybridWebView.SendInvokeMessageToDotNet("HostClassName", "DotNetMethodName", ["param1", 123]);
+
+ // Create a proxy
+ const hostObj = HybridWebView.CreateProxy("host");
+ const result = await hostObj.CallMeFromScriptReturn("Hello", 42);
+
.
+
+
diff --git a/MauiReactJSHybridApp/MainPage.xaml.cs b/MauiReactJSHybridApp/MainPage.xaml.cs
index 802af7e..0805164 100644
--- a/MauiReactJSHybridApp/MainPage.xaml.cs
+++ b/MauiReactJSHybridApp/MainPage.xaml.cs
@@ -13,7 +13,7 @@ public MainPage()
_todoDataStore = new TodoDataStore();
_todoDataStore.TaskDataChanged += OnTodoDataChanged;
- myHybridWebView.JSInvokeTarget = new TodoJSInvokeTarget(this, _todoDataStore);
+ myHybridWebView.ObjectHost.AddObject("host", new TodoJSInvokeTarget(this, _todoDataStore));
BindingContext = this;
}
diff --git a/README.md b/README.md
index 4ce821f..279ff1f 100644
--- a/README.md
+++ b/README.md
@@ -19,8 +19,19 @@ var sum = await myHybridWebView.InvokeJsMethodAsync("JsAddNumbers", 123, 45
And the reverse, JavaScript code calling a .NET method:
+```c#
+// Add an object called 'host' that can be called from JavaScript
+myHybridWebView.ObjectHost.AddObject("host", new MyJSInvokeTarget(this));
+```
+
```js
-HybridWebView.SendInvokeMessageToDotNet("CallMeFromScript", ["msg from js", 987]);
+// Directly call a .Net method called CallMeFromScript
+HybridWebView.SendInvokeMessageToDotNet("host", "CallMeFromScript", ["msg from js", 987]);
+
+// Create a proxy
+const myDotNetObjectProxy = HybridWebView.CreateProxy("host");
+// Call the method, await the result (promise) to get the returned value.
+const result = await myDotNetObjectProxy.CallMeFromScriptReturn("Hello", 42);
```
In addition to method invocation, sending "raw" messages is also supported.