如何在xamarin webview中通过react jsx调用c#代码

how to call c# code via react jsx in xamarin webview

https://docs.microsoft.com/en-us/xamarin/xamarin-forms/app-fundamentals/custom-renderer/hybridwebview

我看到了上面关于xamarin的HybridWebView的msdn文档

我了解与 c# 和 vanillajs 通信的方式

HybridWebViewRenderer.cs

using Android.Content;
using CustomRenderer;
using CustomRenderer.Droid;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;

[assembly: ExportRenderer(typeof(HybridWebView), typeof(HybridWebViewRenderer))]
namespace CustomRenderer.Droid
{
    public class HybridWebViewRenderer : WebViewRenderer
    {
        const string JavascriptFunction = "function invokeCSharpAction(data){jsBridge.invokeAction(data);}";
        Context _context;

        public HybridWebViewRenderer(Context context) : base(context)
        {
            _context = context;
        }

        protected override void OnElementChanged(ElementChangedEventArgs<WebView> e)
        {
            base.OnElementChanged(e);

            if (e.OldElement != null)
            {
                Control.RemoveJavascriptInterface("jsBridge");
                ((HybridWebView)Element).Cleanup();
            }
            if (e.NewElement != null)
            {
                Control.SetWebViewClient(new JavascriptWebViewClient(this, $"javascript: {JavascriptFunction}"));
                Control.AddJavascriptInterface(new JSBridge(this), "jsBridge");
                Control.LoadUrl($"file:///android_asset/Content/{((HybridWebView)Element).Uri}");
            }
        }

        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                ((HybridWebView)Element).Cleanup();
            }
            base.Dispose(disposing);
        }
    }
}

index.html

<html>
<body>
    <script src="http://code.jquery.com/jquery-2.1.4.min.js"></script>
    <h1>HybridWebView Test</h1>
    <br />
    Enter name: <input type="text" id="name">
    <br />
    <br />
    <button type="button" onclick="javascript: invokeCSCode($('#name').val());">Invoke C# Code</button>
    <br />
    <p id="result">Result:</p>
    <script type="text/javascript">function log(str) {
            $('#result').text($('#result').text() + " " + str);
        }

        function invokeCSCode(data) {
            try {
                log("Sending Data:" + data);
                invokeCSharpAction(data);
            }
            catch (err) {
                log(err);
            }
        }</script>
</body>
</html>

但我想不出从 jsx 调用 C# 代码的方式,因为据我了解,为了让 JavaScript 执行 C# 代码,它必须执行一个与 [= JavaScript webview 客户端 class 在 html.

中初始化时传递的字符串状态中的 49=] 函数

通常,如果你在React中使用上面的方法,你会得到一个错误信息。

我不太了解react

如果有人曾经通过 Xamarin webview 处理过 React 页面,请给我一些提示或建议。

当我尝试通过 React,或者当我分离 js 文件并将其导入 html 和 运行 时得到的错误消息

<html>
<body>
    <script src="http://code.jquery.com/jquery-2.1.4.min.js"></script>
    <h1>HybridWebView Test</h1>
    <br />
    Enter name: <input type="text" id="name">
    <br />
    <br />
    <button type="button" onclick="javascript: invokeCSCode('1234');">Invoke C# Code</button>
    <br />
    <p id="result">Result:</p>
    <script type="text/javascript" src="test.js"/>
</body>
</html>

index.html

function invokeCSCode(data) {
    invokeCSharpAction(data);
}

test.js

看到ToolmakerSteve的评论修改了React部分

一开始以为是在react的production build过程中重命名了函数导致找不到函数,结果发现不管用(只是我的误会是[=47=中执行的方法) ] 和在 C# 中执行的方法具有相同的名称。)

我也不知道 eval() 如何在 jsx 中做 <button type="button" onclick="javascript: invokeCSCode(data);" >Invoke C# Code</button> 我想知道我是否可以使用像

这样的方法

下面是我成功从react接收数据的代码示例。

反应部分

TestFunctions.ts

export default function test(data: string){
    eval(`invokeTest(${data})`);
}

SendButton.tsx

export default function Button(props: ButtonProps){
    const dispatch = useAppDispatch()

    const handleClick = () =>{
        test('1234');
    }

    return(
        <ButtonWrapper heightValue={props.heightValue} widthValue={props.widthValue} marginValue={props.marginValue}>
            <Button variant="outlined" onClick={() => handleClick()} style={{height: '56px', width: '100%', fontSize: 'large'}}>
                send
            </Button>
        </ButtonWrapper>
    )
}

Xamarin 部分

WebViewer.cs

public class WebViewer : WebView
{
    public static readonly BindableProperty UriProperty = BindableProperty.Create(
            propertyName: "Uri",
            returnType: typeof(string),
            declaringType: typeof(WebViewer),
            defaultValue: default(string),
            propertyChanged: UriChanged);

    private static void UriChanged(BindableObject bindable, object oldValue, object newValue)
    {
         if (newValue != null && bindable is WebViewer webViewer)
         {
            if(webViewer.CookieList != null && (string)newValue == (string)oldValue)
            {
                Uri uri = new Uri((string)newValue, UriKind.RelativeOrAbsolute);

                CookieContainer cookieContainer = new CookieContainer();

                foreach (Cookie cookie in webViewer.CookieList)
                {
                    cookie.Domain = uri.Host;
                    cookie.Path = uri.PathAndQuery;
                    cookieContainer.Add(cookie);
                }
                webViewer.Cookies = cookieContainer;
             }
             webViewer.Source = (string)newValue;
          }
     }

     public string Uri
     {
         get => (string)GetValue(UriProperty);
         set => SetValue(UriProperty, value);
     }

     public static readonly BindableProperty CookieListProperty = BindableProperty.Create(
            propertyName: "Cookies",
            returnType: typeof(List<Cookie>),
            declaringType: typeof(WebViewer),
            defaultValue: null,
            propertyChanged: CookieListChanged);
        
     public List<Cookie> CookieList
     {
         get => (List<Cookie>)GetValue(CookieListProperty);
         set => SetValue(CookieListProperty, value);
     }
        
     private static void CookieListChanged(BindableObject bindable, object oldValue, object newValue)
     {
         if (newValue != null && bindable is WebViewer webViewer)
         {
             Uri uri = new Uri(webViewer.Uri, UriKind.RelativeOrAbsolute);

             CookieContainer cookieContainer = new CookieContainer();

             foreach (Cookie cookie in (List<Cookie>)newValue)
             {
                 cookie.Domain = uri.Host;
                 cookie.Path = uri.PathAndQuery;
                 cookieContainer.Add(cookie);
             }
             webViewer.Cookies = cookieContainer;

             if (webViewer.Uri != default(string))
             {
                 webViewer.Source = webViewer.Uri;
             }
         }
     }

     Action<string> action;
     public void RegisterAction(Action<string> callback)
     {
         action = callback;
     }

     public void Cleanup()
     {
         action = null;
     }

     public void InvokeAction(string data)
     {
         if (action == null || data == null)
         {
             return;
         }
         action.Invoke(data);
     }
}

WebViewerRenderer.cs

using Android.Content;
using Android.Views;
using Android.Webkit;
using BDApp.Mobile.Droid.CustomRenderer;
using BDApp.Mobile.Views;
using Java.Interop;
using System;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;

[assembly: ExportRenderer(typeof(WebViewer), typeof(WebViewerRenderer))]
namespace BDApp.Mobile.Droid.CustomRenderer
{
    public class WebViewerRenderer : WebViewRenderer
    {
        const string JavascriptFunction = "function invokeTest(data){jsBridge.invokeAction(data);}";
        public WebViewerRenderer(Context context) : base(context)
        {
        }

        protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.WebView> e)
        {
            base.OnElementChanged(e);

            if(e.OldElement != null)
            {
                Control.RemoveJavascriptInterface("jsBridge");
                ((WebViewer)Element).Cleanup();
            }
            if(e.NewElement != null)
            {
                Control.SetWebViewClient(new JavascriptWebViewClient(this, $"javascript: {JavascriptFunction}"));
                Control.AddJavascriptInterface(new JSBridge(this), "jsBridge");
            }
            Control.Settings.SetAppCacheEnabled(false);
            Control.Settings.CacheMode = CacheModes.NoCache;
        }

        public override bool DispatchTouchEvent(MotionEvent e)
        {
            Parent.RequestDisallowInterceptTouchEvent(true);
            return base.DispatchTouchEvent(e);
        }

        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                ((WebViewer)Element).Cleanup();
            }
            base.Dispose(disposing);
        }
    }
    public class JavascriptWebViewClient : FormsWebViewClient
    {
        string _javascript;

        public JavascriptWebViewClient(WebViewerRenderer renderer, string javascript) : base(renderer)
        {
            _javascript = javascript;
        }

        public override void OnPageFinished(Android.Webkit.WebView view, string url)
        {
            base.OnPageFinished(view, url);
            view.EvaluateJavascript(_javascript, null);
        }
    }
    public class JSBridge : Java.Lang.Object
    {
        readonly WeakReference<WebViewerRenderer> hybridWebViewRenderer;

        public JSBridge(WebViewerRenderer hybridRenderer)
        {
            hybridWebViewRenderer = new WeakReference<WebViewerRenderer>(hybridRenderer);
        }

        [JavascriptInterface]
        [Export("invokeAction")]
        public void InvokeAction(string data)
        {
            WebViewerRenderer hybridRenderer;

            if (hybridWebViewRenderer != null && hybridWebViewRenderer.TryGetTarget(out hybridRenderer))
            {
                ((WebViewer)hybridRenderer.Element).InvokeAction(data);
            }
        }
    }
}

TestWebViewPage.xaml

<StackLayout>
        <views:WebViewer Uri="{Binding Uri}" x:Name="webView" CookieList="{Binding CookieList}"
                         HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand"/>
</StackLayout>