Viktor_DE Viktor_DE - 3 months ago 41
C# Question

How to receive scriptNotify from html in appdata on UWP c#

Windows won't let my WebView_ScriptNotify Event receive a call if the html is loaded from ms-appdata.

I'm aware that I can use the ms-appx-web protocol to load such a file from my app bundle, but this is no option because the data to show are downloaded after install of the app.
I also can't just use webView.navigateToString because this won't include the referenced libraries in the html file.

Currently I'm trying something like this in my Class.xaml.cs

WebView webView = new WebView();
webView.ScriptNotify += WebView_ScriptNotify;
Uri navigationUri = new Uri(@"ms-appdata:///local/index.html");
webView.Navigate(navigationUri);


and

private void WebView_ScriptNotify(object sender, NotifyEventArgs e)
{
System.Diagnostics.Debug.WriteLine("ScriptNotifyValue: " + e.Value);
//I want to do the magic here, but this will never be called
}


in the html file is

<div id="content">
<div class="btn" onClick="window.external.notify('hello world');"</div>
</div>


Furthermore, it's no option to use InvokeScript(), because I don't know when the event must be fired and the values for it.

Yet it's mandatory to use files from ms-appdata.

Do you know a solution for this?
Even an alternative workaroung would amaze me.

Answer

Ref Script notify changes in XAML:

For content to be able to send notifications the following conditions apply:

  • The source of the page should be from the local system via NavigateToString(), NavigateToStream() or ms-appx-web:///

Or

  • The source of the page is delivered via https:// and the site domain name is listed in the app content URI’s section of the package manifest.

So to solve this issue, we can use WebView.NavigateToLocalStreamUri method with the protocol ms-local-stream://, rather than ms-appdata://. For example:

public sealed partial class MainPage : Page
{
    public MainPage()
    {
        this.InitializeComponent();
    }

    protected override void OnNavigatedTo(NavigationEventArgs e)
    {
        // The 'Host' part of the URI for the ms-local-stream protocol needs to be a combination of the package name
        // and an application-defined key, which identifies the specific resolver, in this case 'MyTag'.

        Uri url = webView.BuildLocalStreamUri("MyTag", "index.html");
        StreamUriWinRTResolver myResolver = new StreamUriWinRTResolver();

        // Pass the resolver object to the navigate call.
        webView.NavigateToLocalStreamUri(url, myResolver);
    }

    private void webView_ScriptNotify(object sender, NotifyEventArgs e)
    {
        System.Diagnostics.Debug.WriteLine("ScriptNotifyValue: " + e.Value);
    }
}

public sealed class StreamUriWinRTResolver : IUriToStreamResolver
{
    public IAsyncOperation<IInputStream> UriToStreamAsync(Uri uri)
    {
        if (uri == null)
        {
            throw new Exception();
        }
        string path = uri.AbsolutePath;

        // Because of the signature of the this method, it can't use await, so we
        // call into a seperate helper method that can use the C# await pattern.
        return GetContent(path).AsAsyncOperation();
    }

    private async Task<IInputStream> GetContent(string path)
    {
        // We use app's local folder as the source
        try
        {
            Uri localUri = new Uri("ms-appdata:///local" + path);
            StorageFile f = await StorageFile.GetFileFromApplicationUriAsync(localUri);
            IRandomAccessStream stream = await f.OpenAsync(FileAccessMode.Read);
            return stream;
        }
        catch (Exception) { throw new Exception("Invalid path"); }
    }
}

For more info, please see Remarks and Examples in WebView.NavigateToLocalStreamUri method and also Custom URI resolving in What’s new in WebView in Windows 8.1. Besides, there is also a WebView control (XAML) sample on GitHub.

Comments