1

So I'm trying to open up an HTML file through a WPF UI(XAML) hyperlink and pass some coordinates to the HTML file, which uses JavaScript to create a map that centers around, hopefully, the coordinates I'm trying to pass. coord1 and coord2 are doubles.

private void Hyperlink_RequestNavigate(object sender, RequestNavigateEventArgs e)
    {
        String coords = coord1 + " " + coord2;
        Process.Start(new ProcessStartInfo(e.Uri.AbsoluteUri, coords));
        e.Handled = true;
    }

This is the JavaScript end where I'm trying to receive the variable coords:

<script type='text/javascript'>
    var map;
    var coords = '<%=coords%>';

   function GetMap() {

    map = new Microsoft.Maps.Map('#myMap', {center: new Microsoft.Maps.Location(55, 0)});
    ...

I got '<%=coords%>' from online but it literally just returns <%=coords>. I'm trying to take the two coordinate points to replace the 55 and 0.

I didn't post the XAML code, but the C# code opens up the HTML file when a hyperlink is clicked and I believe the string "coords" is being passed with the data, but I just don't know how to receive it.

Heretic Monkey
  • 11,687
  • 7
  • 53
  • 122
Marcus
  • 116
  • 11
  • 2
    If you have control of the web site then pass the parameters in the URL query string – Crowcoder Jul 02 '22 at 00:01
  • can you try this one? [link](https://stackoverflow.com/questions/39547973/write-query-string-into-html-document) – Bala Jul 02 '22 at 07:17
  • @Bala Sorry, it does not – Marcus Jul 05 '22 at 16:32
  • Does this answer your question? [How to communicate between WPF and JavaScript in a WebBrowser instance?](https://stackoverflow.com/questions/12052992/how-to-communicate-between-wpf-and-javascript-in-a-webbrowser-instance) – Heretic Monkey Jul 05 '22 at 19:55

1 Answers1

0

There are two ways to pass data into a HTML web page from a WPF app, assuming you control the source code of the web page. Note that <%=coords%> is an ASP.NET thing that passes information from the code behind to the front end on load and would require a lot more work to get working (there are easier options).

Method 1

If you only want one way communication (pass into the web page, but don't need to send anything back to WPF C#), you can pass the information in as a query string, then parse that when your application loads. This is really easy. Here is an example of a web page that takes in a URL with optional center and zoom parameters the look like this test.html?center=51.50632,-0.12714&zoom=15

<!DOCTYPE html>
<html lang="en">
<head>
    <title></title>

    <meta charset="utf-8" />
    <meta charset="utf-8" http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
</head>
<body>

    <div id='myMap' style='width: 100vw; height: 100vh;'></div>

<script>
    var map;
    
    function GetMap(){  
        //Default center/zoom values.
        let center = new Microsoft.Maps.Location(0, 0);
        let zoom = 1;

        //Get the queryStringuery string of the URL. 
        const queryString = window.location.search;
        const urlParams = new URLSearchParams(queryString);
        
        //Extract center infromation from query string. 
        if (urlParams.has('center')) {
            //Parse coordinates from the center parameter. Assume that string is in the format "latitude, longitude". Comma/space optional, must have atleast one. 
            let parts = urlParams.get('center').split(/[,\s]+/gi);
            
            if(parts.length >= 2){
                let lat = parseFloat(parts[0]);
                let lon = parseFloat(parts[1]);
                
                if(!isNaN(lat) && !isNaN(lat)){
                    center = new Microsoft.Maps.Location(lat, lon);
                }
            }
        } 
        
        if (urlParams.has('zoom')) {
            let z = parseInt(urlParams.get('zoom'));
            
            if(!isNaN(z)){
                zoom = z;
            }   
        }
        
         map = new Microsoft.Maps.Map('#myMap', {
            center: center,
            zoom: zoom
         });
    }
</script>
<script type='text/javascript' src='https://www.bing.com/api/maps/mapcontrol?callback=GetMap&key=[Your Bing Maps Key]' async defer></script>
</body>
</html>

The main limitations of this approach are:

  • You can only pass as much information as the max URL length will allow (2083 characters).
  • Information is only sent once, when the page loads. If you want to send new information, then the page has to be reloaded (inefficient, and can generate additional map sessions when using Bing Maps).
  • No way to send information back to the WPF app.

Method 2

If you want to two-way communication, or want to avoid the limitations of method 1 you can wrap a web browser control in your application and make use of interoperability. Currently WPF provides WebView2 which allows you to embed the Edge browser into your application https://learn.microsoft.com/en-us/microsoft-edge/webview2/get-started/wpf Here is a great detailed blog post on how to do this: https://weblog.west-wind.com/posts/2021/Jan/26/Chromium-WebView2-Control-and-NET-to-JavaScript-Interop-Part-2

Alternatively, you can also use Chromium/CefSharp (https://cefsharp.github.io/) rather than WPF's WebView2 control (it likely will make your application a lot larger since it won't leverage the browser that's already installed on the system: https://dev.to/noseratio/comparing-process-working-sets-of-webview-based-windows-desktop-apps-5dkk).

Update: Method 3

You can read your HTML file as a string and do a find and replace on your placeholder. In the below example I've made the HTML file an embedded resource (likely what you will need to do if you want to ship this to others at some point). I called it mapPage.html and have it in the root directory of the project. Add the html file to your project, go to properties and set build action to embedded resource. To read it you will need to include the namespace of your project in the file name path, in my case the namespace is SimpleWebBrowser, thus the file name would when reading would be SimpleWebBrowser.mapPage.html. Then in your C# code you could have a method like this:

private void LoadMap(double latitude, double longitude)
{
    Assembly assembly = Assembly.GetExecutingAssembly();
    using (var reader = new StreamReader(assembly.GetManifestResourceStream("SimpleWebBrowser.mapPage.html")))
    {
        var html = reader.ReadToEnd();
        html = html.Replace("<%=coords%>", latitude + " " + longitude);

        myBrowser.NavigateToString(html);
    }
}

You HTML code would then handle the input like this:

<script type='text/javascript'>
    var map;
    var coords = '<%=coords%>';

    function GetMap() {
        //Default center.
        var center = new Microsoft.Maps.Location(55, 0);
       
        //Parse the coords string and get lat/lon values.
        var parts = coords.split(' ');
       
        if(parts.length >= 2){
            let lat = parseFloat(parts[0]);
            let lon = parseFloat(parts[1]);

            if(!isNaN(lat) && !isNaN(lat)){
                center = new Microsoft.Maps.Location(lat, lon);
            }
        }
     
        map = new Microsoft.Maps.Map('#myMap', {center: });
    ...
rbrundritt
  • 16,570
  • 2
  • 21
  • 46
  • what does "assuming you control source code of the web page" mean? I'm trying to use a file path like C:\Users\intern3\source\repos\MarketingProject\Samples\WPF\RESTToolkitTestApp\index.htm I don't think adding a query string works for this kind of file, if that's what you mean? Are there other ways to pass data going along the lines of method 1? Thanks. – Marcus Jul 05 '22 at 21:06
  • That means you can edit the source code of the HTML file, which it appears is the case. As for using a query string with a local file, I'm sure there is a way but I can't find any examples. But this did give me an idea for a 3rd method, read the file as a string, put a placeholder in your html file, then find an replace it with your values. Then pass the HTML into the browser. I'll post an update to my answer to include more details on this. – rbrundritt Jul 05 '22 at 21:36
  • I'm getting this error: System.ArgumentNullException: 'Value cannot be null. Parameter name: stream' System.ArgumentNullException: 'Value cannot be null. Parameter name: stream' – Marcus Jul 05 '22 at 23:33
  • I'm not very sure, I'm not very used to Visual Studio or WebBrowser or anything to be honest, but I think the problem is that I need to load data into the htm file before it ever navigates to the file. Correct me if I'm wrong but the "replace(<%=coords%>)" line is supposed to change data after it's opened? If I could just change data once the htm file is opened in the WebBrowser object, couldn't I just manually load data in? I've used things like ProcessInfo before which allows you to pass String parameters, it's just that I don't know how to access it once the htm file opens. – Marcus Jul 05 '22 at 23:40
  • For your first issue, likely an issue reading your file. Make sure the file is in your project and the build action property on it is set to Embedded Resource. Then, in the code, make sure the structure of the resource name is `"[your projects namespace].[html file name]"` or if you put the file in a folder `"[your projects namespace].[folder name].[html file name]"` – rbrundritt Jul 06 '22 at 16:05
  • For your second comment, the code in method 3 does change the data in the HTML file with the replace call. It replaces the `<%=coords%>` with the coordinates you pass in as a string. Method 3 lets you change the contents of the HTML file before it is loaded. Method 2, lets you change the things after the html file is loaded. You can call JavaScript functions from C# and C3 methods from JavaScript. In your functions you would add logic to determine how to handle the message being sent from one to the other. – rbrundritt Jul 06 '22 at 16:08