7

In my WPF application, I am successfully able to communicate a message from WPF to JavaScript using the InvokeScript(String, Object[]) method of WebBrowser. I am passing my message in Object[] parameter and it is well received by JS code. (See the code below)

How can I achieve the same thing in WebView2? Is there any way we can pass on the parameters to the JS Code just like we do in WebBrowser control?

Window.xaml

<Grid>
    <DockPanel> 
        <StackPanel>        
        <TextBox x:Name="txtMessageFromWPF" Width="150" FontSize="20"></TextBox>
        <Button x:Name="btnCallDocument" Click="btnCallDocument_Click" Content="CallDocument" />
        </StackPanel>
        <WebBrowser x:Name="webBrowser" DockPanel.Dock="Top" Margin="30"/>           
    </DockPanel>
</Grid>

Window.xaml.cs

private void btnCallDocument_Click(object sender, RoutedEventArgs e)
{
    webBrowser.InvokeScript("WriteMessageFromWPF", new object[] { this.txtMessageFromWPF.Text });
}

JS Code:

<script type="text/javascript">
    function getAlert()
    {
        alert("Hi the page is loaded!!!");
    }
    window.onload = getAlert;
    
    function WriteMessageFromWPF(message){
        document.write("Message from WPF: " + message);
    }
</script>
Uwe Keim
  • 39,551
  • 56
  • 175
  • 291
DotNetSpartan
  • 931
  • 4
  • 20
  • 41

2 Answers2

10

Update: Version 2 of the extension is much simpler and more universal. It uses JSON as 'middle-station'- use NewtonSoft or the built-in JSON converter.

Here's an extension I have made (just create a class file and paste it).

The ExecuteScriptFunctionAsync builds the string necessary for ExecuteScriptAsync and then executes it:

//using Microsoft.Web.WebView2.WinForms; //Uncomment the one you use
//using Microsoft.Web.WebView2.Wpf; //Uncomment the one you use
using Newtonsoft.Json;
using System.Threading.Tasks;

public static class Extensions
{
    public static async Task<string> ExecuteScriptFunctionAsync(this WebView2 webView2, string functionName, params object[] parameters)
    {
        string script = functionName + "(";
        for (int i = 0; i < parameters.Length; i++)
        {
            script += JsonConvert.SerializeObject(parameters[i]);
            if (i < parameters.Length - 1)
            {
                script += ", ";
            }
        }
        script += ");";
        return await webView2.ExecuteScriptAsync(script);
    }
}

Pass the javascript function name as first parameter, followed by the function parameters.

The code makes it possible to have any number of parameters of all types that can be srialized to JSON: object, array, string, all numbers, boolean etc.

Example of use (from the question):

private async void btnCallDocument_Click(object sender, RoutedEventArgs e)
{
    await webBrowser.ExecuteScriptFunctionAsync("WriteMessageFromWPF", this.txtMessageFromWPF.Text);
}

Another example (this will scroll the window to bottom):

await webView21.ExecuteScriptFunctionAsync("window.scrollTo", 0, 10000);
Poul Bak
  • 10,450
  • 5
  • 32
  • 57
  • Would your extension (or something similar) work for built-in `WebView2` in [WinUI 3.0](https://learn.microsoft.com/en-us/windows/apps/winui/winui3/) with `UWP` - for example in [this](https://stackoverflow.com/q/62869094/1232087) scenario? – nam Jul 13 '20 at 20:24
  • @nam: I have no knowledge of WinUI, I suggest you try it. You need to find the 'using' directive for WinUI WebView2 – Poul Bak Jul 13 '20 at 22:02
  • Keep in mind that it really does try and turn EVERYTHING into JSON, even if you return just one string. It will surround it by quotes and javascript escape it. – Brain2000 Aug 07 '20 at 20:30
  • That is true. When 'ExecuteScriptAsync ' returns you will always have a JSON string, which must be deserialized. The same goes for 'WebMessageReceived'. – Poul Bak Aug 07 '20 at 20:56
  • How would you pass it back to JS? – spacemonki Jun 28 '21 at 22:36
  • @spacemonki, you use javascript functions, which is what you call with this method. When the javascript function runs, you can of course save the variables in JS. – Poul Bak Jun 28 '21 at 23:08
2

There is currently no direct equivalent of InvokeScript in WebView2. The closest is CoreWebView2.ExecuteScriptAsync. This method takes a string of script to run and asynchronously returns the result of the script execution as a JSON string.

If you want to call a function you would need to create a string of script to do that and give it to ExecuteScriptAsync. In your example that means producing the string WriteMessageFromWPF("...") and providing that to ExecuteScriptAsync.

The tricky part is correctly encoding your function parameter. If the string parameter has a quote in it, then you would need to slash escape the quote or else produce incorrect script. You should be able to slash escape your quotes using String.Replace:

string script = "WriteMessageFromWPF(\"" + 
    this.txtMessageFromWPF.Text.Replace("\"", "\\\"")
    + "\")";
await webview2.CoreWebView2.ExecuteScriptAsync(script);
David Risney
  • 3,886
  • 15
  • 16
  • I have similar scenario posted [here](https://stackoverflow.com/q/66848156/1232087) in case someone has any suggestion/comments. – nam Mar 29 '21 at 03:05