Hi
I have got a working HybridWebview used in my mainView and I have added a back button that I would like to bind to the hybridWebView History implementing cangoback and goback, so the button can be enable accordingly. I have done similar things with other buttons and using commands in the viewmodel, but the goback method is a tricky one for me. I would like to use it on Android and iOS and I am very stuck.
This is my typical HybridWebView based on Microsoft guidelines:
public class HybridWebView : WebView
{
private Action<string, string> _action;
public static readonly BindableProperty UriProperty = BindableProperty.Create(
propertyName: "Uri",
returnType: typeof(string),
declaringType: typeof(HybridWebView),
defaultValue: default(string));
public string Uri
{
get { return (string)GetValue(UriProperty); }
set { SetValue(UriProperty, value); }
}
public static readonly BindableProperty IsLoggedInProperty = BindableProperty.Create(
propertyName: "IsLoggedIn",
returnType: typeof(bool),
declaringType: typeof(HybridWebView),
defaultValue: default(bool));
public bool IsLoggedIn
{
get { return (bool)GetValue(IsLoggedInProperty); }
set { SetValue(IsLoggedInProperty, value); }
}
public void RegisterAction(Action<string, string> callback)
{
_action = callback;
}
public void Cleanup()
{
_action = null;
}
public void InvokeAction(string param1, string param2)
{
if (_action == null || (param1 == null && param2 == null))
{
return;
}
if (MainThread.IsMainThread)
_action.Invoke(param1, param2);
else
MainThread.BeginInvokeOnMainThread(() => _action.Invoke(param1, param2));
}
}
Android Renderer:
public class HybridWebViewRenderer : WebViewRenderer
{
private const string JavascriptFunction = "function invokeXamarinFormsAction(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");
}
}
}
Android Client:
public class JavascriptWebViewClient : WebViewClient
{
readonly string _javascript;
readonly HybridWebViewRenderer _renderer;
public JavascriptWebViewClient(HybridWebViewRenderer hybridWebViewRenderer, string javascript)
{
_javascript = javascript;
_renderer = hybridWebViewRenderer;
}
public override void OnReceivedSslError(Android.Webkit.WebView view, SslErrorHandler handler, Android.Net.Http.SslError error)
{
//base.OnReceivedSslError(view, handler, error);
handler.Proceed();
}
public override void OnPageStarted(WebView view, string url, Android.Graphics.Bitmap favicon)
{
base.OnPageStarted(view, url, favicon);
}
public override void OnPageFinished(WebView view, string url)
{
base.OnPageFinished(view, url);
view.EvaluateJavascript(_javascript, null);
if (_renderer != null)
{
((Controls.HybridWebView)_renderer.Element).IsLoggedIn = HasRotaOneCookie(url);
}
}
.....
iOS Renderer:
public class HybridWebViewRenderer : WkWebViewRenderer, IWKScriptMessageHandler
{
private const string JavaScriptFunction = "function invokeXamarinFormsAction(data){window.webkit.messageHandlers.invokeAction.postMessage(data);}";
private WKUserContentController _userController;
public HybridWebViewRenderer() : this(new WKWebViewConfiguration())
{
}
public HybridWebViewRenderer(WKWebViewConfiguration config) : base(config)
{
_userController = config.UserContentController;
var script = new WKUserScript(new NSString(JavaScriptFunction), WKUserScriptInjectionTime.AtDocumentEnd, false);
_userController.AddUserScript(script);
_userController.AddScriptMessageHandler(this, "invokeAction");
}
protected override void OnElementChanged(VisualElementChangedEventArgs e)
{
base.OnElementChanged(e);
if (e.OldElement != null)
{
_userController.RemoveAllUserScripts();
_userController.RemoveScriptMessageHandler("invokeAction");
HybridWebView hybridWebViewMain = e.OldElement as HybridWebView;
hybridWebViewMain?.Cleanup();
}
if (e.NewElement != null)
{
//// No need this since we're loading dynamically generated HTML content
//string filename = Path.Combine(NSBundle.MainBundle.BundlePath, $"Content/{((HybridWebView)Element).Uri}");
//LoadRequest(new NSUrlRequest(new NSUrl(filename, false)));
this.NavigationDelegate = new NavigationDelegate(this);
}
}
public void DidReceiveScriptMessage(WKUserContentController userContentController, WKScriptMessage message)
{
var dataBody = message.Body.ToString();
if (dataBody.Contains("|"))
{
var paramArray = dataBody.Split("|");
var param1 = paramArray[0];
var param2 = paramArray[1];
((HybridWebView)Element).InvokeAction(param1, param2);
}
else
{
((HybridWebView)Element).InvokeAction(dataBody, null);
}
}
}
public class NavigationDelegate : WKNavigationDelegate
{
HybridWebViewRenderer _renderer;
public NavigationDelegate(HybridWebViewRenderer renderer)
{
this._renderer = renderer;
}
public override void DidFinishNavigation(WKWebView webView, WKNavigation navigation)
{
base.DidFinishNavigation(webView, navigation);
HybridWebView webview = _renderer.Element as HybridWebView;
webview.IsLoggedIn = HasRotaOneCookie();
}
.....
iOS Delegate:
[Register("AppDelegate")]
public partial class AppDelegate : global::Xamarin.Forms.Platform.iOS.FormsApplicationDelegate
{
//
// This method is invoked when the application has loaded and is ready to run. In this
// method you should instantiate the window, load the UI into it and then make the window
// visible.
//
// You have 17 seconds to return from this method, or iOS will terminate your application.
//
public override bool FinishedLaunching(UIApplication app, NSDictionary options)
{
global::Xamarin.Forms.Forms.Init();
LoadApplication(new App());
return base.FinishedLaunching(app, options);
}
}
Then in the shared code, I have got the typical mainViewModel, how can I bind the back button from my view to do webVIew.Goback and being enabled if webView.CangoBack is true?
Thanks.