1

I'm starting development of mobile application connected to web project that let's patients take notes of their bleedings. On web the user can click and select certain places on few human figures. One with some selection looks like this

enter image description here

I don't really think this would be easy to achieve using standard controls(?), so I thought it would be the easiest to use a WebView that would host the piece of code that handles selection.

To set the locations e.g when user wants to edit old bleeding I could just follow Xamarin guide on how to call JS from C#, however, the return value is void. I haven't been able to find a way how to get data from Xamarin.Forms.WebView. Any suggestions?

pikausp
  • 1,142
  • 11
  • 31

1 Answers1

0

This highly depends on what platforms and version of platforms your are trying to target.

If you really want to use a WebView, you might check out the XLabs Hybrid WebView which does something similar I believe.

Other than XLabs' methods, you can easily get back a string from an iOS renderer using the UIWebView.EvaluateJavascript() method and then you can always override WebChromClient.OnJsAlert in a Xamarin Android WebView renderer (link), which I have successfully used back to Android 4.1.

Something like (Let me know if something does not work or is missing, I typed this out and removed extra code so there may be minor issues)...

Xamarin Forms Code:

public class SomethingWebView : WebView { }

iOS WebView Renderer with UIWebViewDelegate:

[assembly: Xamarin.Forms.ExportRenderer(typeof(SomethingWebView), typeof(Your.Droid.Namespace.SomethingWebViewRenderer))]

namespace Your.Droid.Namespace.Renderer {

public class SomethingWebViewRenderer : WebViewRenderer {

    protected override void OnElementChanged(VisualElementChangedEventArgs e) {
        base.OnElementChanged(e);

        if(e.OldElement == null) {
            Delegate = new SomethingWebViewDelegate();
        }
    }
}

internal class SomethingWebViewDelegate : UIWebViewDelegate {

    public override void LoadingFinished(UIWebView webView) {
        string something = webView.EvaluateJavascript("(function() { return 'something'; })()");
    }
}
}

Android WebView Renderer with WebViewClient and WebChromeClient:

[assembly: Xamarin.Forms.ExportRenderer(typeof(SomethingWebView), typeof(Your.iOS.Namespace.SomethingWebViewRenderer))]

namespace Your.iOS.Namespace.Renderer {

public class SomethingWebViewRenderer : WebViewRenderer {

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

        base.OnElementChanged(e);

        if(e.OldElement == null) {

            Control.Settings.JavaScriptEnabled = true;

            Control.SetWebViewClient(new SomethingWebViewClient());

            Control.SetWebChromeClient(new SomethingWebChromeClient());
        }
    }
}

internal class SomethingWebViewClient : WebViewClient {

    public override void OnPageFinished(Android.Webkit.WebView view, string url) {
        base.OnPageFinished(view, url);

        view.LoadUrl("javascript:{(function() { window.alert('something'); })()};");
    }
}

public class SomethingWebChromeClient : WebChromeClient {

    public override bool OnJsAlert(Android.Webkit.WebView view, string url, string message, JsResult result) {

        if(message.StartsWith("something") { //This is where you would look for your magic string, anything starting with your magic string is what you would extract and/or act on
            //Do something....

            result.Cancel(); //This cancels the JS alert (there are other talked about methods of doing this but this works for me)
            return true; //This tells the WebView "we got this"
        }

        return base.OnJsAlert(view, url, message, result); //Let the WebView handle this since we did not find out special string
    }
}
}

Finally, another method I looked at involved using JavaScript to redirect the page to a new URL, then in the event that is used to tell the WebView whether or not it should go forward with the redirect, you pull out the WebView's current URL and, if you find your magic string, you do something. This would have worked great except for the fact that URLs can only be like 255 characters long so it would not work for my needs.

Community
  • 1
  • 1
hvaughan3
  • 10,955
  • 5
  • 56
  • 76
  • If I wasn't to use the XLabs I would need to design UI separately for Android & iOS, rigth? The application doesn't really have platform specific requirements and this is the only non-standard thing. – pikausp Sep 20 '16 at 10:29
  • Oh nevermind, from reading https://developer.xamarin.com/guides/xamarin-forms/custom-renderer/hybridwebview/ it looks like we define the HybridView in the forms project and then just platform specific renderers (that you showed here) in the correct platform projects, correct? – pikausp Sep 20 '16 at 11:29
  • @pikausp If you use Xamarin Forms, you can create a single UI which will be used on both platforms. Also, that is correct about defining the advanced functionality in platform specific code and referencing it in your shared project using either the hybrid `WebView` or using the other methods I described. – hvaughan3 Sep 20 '16 at 12:55
  • Correct me if I'm wrong, but aren't your solutions platform specific, as in I'd need to define the UI in platform specific projects to use them? – pikausp Sep 20 '16 at 13:02
  • @pikausp The code I showed you is platform specific but it is written in a way that it can be referenced from a Xamarin Forms platform. Let me finished the example real quick. – hvaughan3 Sep 20 '16 at 13:30
  • @pikausp Check out the example now. I added the Xamarin Forms `WebView` class that you would need. I also added the ExportRenderer annotations **above the namespace** declaration, which allows your Xamarin Forms `SomethingWebView` to connect to the custom renderers. See [this link](https://developer.xamarin.com/guides/xamarin-forms/custom-renderer/hybridwebview/) for more info on custom renderers and how they work. – hvaughan3 Sep 20 '16 at 13:35
  • That's what I meant.Thanks great! – pikausp Sep 20 '16 at 14:59