如何在xamarin webview中通过react jsx调用c#代码
how to call c# code via react jsx in xamarin webview
我看到了上面关于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>
我看到了上面关于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>