locked
Call C# method from javascript in WebView and retrieve the return value

    Question

  • The issue I am facing is the following :

    I want to call a C# method from the javascript in my WebView and get the result of this call in my javascript.

    In an WPF Desktop application, it would not be an issue because a WebBrowser (the equivalent of a webView) has a property ObjectForScripting to which we can assign a C# object containg the methods we want to call from the javascript.

    In an Windows Store Application, it is slightly different because the WebView only contains an event ScriptNotify to which we can subscribe like in the example below :

    void MainPage_Loaded(object sender, RoutedEventArgs e)
    {
         this.webView.ScriptNotify += webView_ScriptNotify;
    }

    When the event ScriptNotify is raised by calling window.external.notify(parameter), the method webView_ScriptNotify is called with the parameter sent by the javascript :

    function CallCSharp() {
        var returnValue;
        window.external.notify(parameter);
        return returnValue;
    }
            private async void webView_ScriptNotify(object sender, NotifyEventArgs e)
            {
                var parameter = e.Value;
                var result = DoSomerthing(parameter);
            }

    The issue is that the javascript only raise the event and doesn't wait for a return value or even for the end of the execution of webView_ScriptNotify().

    A solution to this issue would be call a javascript function from the webView_ScriptNotify() to store the return value in a variable in the javascript and retrieve it after.

    private async void webView_ScriptNotify(object sender, NotifyEventArgs e)
    {
         var parameter = e.Value;
         var result = ReturnResult();
         await this.webView.InvokeScriptAsync("CSharpCallResult", new string[] { result });
    }
    var result;
    
    function CallCSharp() {
        window.external.notify(parameter);
        return result;
    }
    
    function CSharpCallResult(parameter){
        result = parameter;
    }
    However this does not work correctly because the call to the CSharpResult function from the C# happens after the javascript has finished to execute the CallCSharp function. Indeed the window.external.notify does not wait for the C# to finish to execute.

    Do you have any solution to solve this issue ? It is quite strange that in a Window Store App it is not possible to get the return value of a call to a C# method from the javascript whereas it is not an issue in a WPF application.
    Friday, March 06, 2015 4:57 PM

Answers

  • There is no way around this. You will need to refactor the calls:

    The initial call starts the process by calling window.external.notify();

    The response comes in later via InvokeScriptAsync.

    You may be able to encapsulate this so that InvokeScriptAsync sets the "returned" value somewhere that your CallCSharp() function can get to it, but that may need extra synchronization (I'm not sure if everything will run synchronously on the same thread or not).

    Friday, March 13, 2015 1:38 AM
    Owner

All replies

  • You're on the right track with InvokeScriptAsync and ScriptNotify.

    Since there isn't any way to synchronize this you'll need to implement a completion pattern. Split your original JS function into two functions. The first will end when it calls window.external.notify, then the InvokeScriptAsync can call the second to finish off the necessary processing. 

    Saturday, March 07, 2015 1:13 AM
    Owner
  • I am not very familiar with the completion pattern and I could not find many things about it on Google, what is the principle? Unfortunately your solution of splitting my JS function does not work in my case. In my example my  CallCSharp function is called by another function which provides the parameter and needs the return value to directly use it. This function which called CallCSharp will always execute before an InvokeScriptAsync call a second function. Furthermore, the content of my WebView is used in a cross platforms context so actually my CallCSharp function more look like the code below.

    function CallCSharp() { if (isAndroid()) { //mechanism to call C# method from js in Android //return the result of call to C# method } else if (isWindowsStoreApp()) { window.external.notify(parameter); // should return the result of call to C# method } else { } }

    In android, the mechanism in the webView allows to return the value of the call to a C# method, in a WPF application also. But I can't figure why for a Windows Store App we don't have this possibility.
    Saturday, March 07, 2015 12:41 PM
  • There is no way around this. You will need to refactor the calls:

    The initial call starts the process by calling window.external.notify();

    The response comes in later via InvokeScriptAsync.

    You may be able to encapsulate this so that InvokeScriptAsync sets the "returned" value somewhere that your CallCSharp() function can get to it, but that may need extra synchronization (I'm not sure if everything will run synchronously on the same thread or not).

    Friday, March 13, 2015 1:38 AM
    Owner