简体   繁体   中英

How to pass information from a WPF app to an HTML page

I got a WPF application which finds locations of latitude and longitude, and display them to the user inside the app. The app got a button that when clicked, is supposed to open a map using the Google maps API and display these locations on the map using markers.

I got this below script inside an HTML which as of now just opens a map on a fixed location and displaying just a sample marker.

<script>
  function initMap() {
    let myLatLng = {lat: -25.363, lng: 131.044};

    let map = new google.maps.Map(document.getElementById('map'), {
      zoom: 4,
      center: myLatLng
    });

    let marker = new google.maps.Marker({
      position: myLatLng,
      map: map,
      title: 'TITLE!'
    });
  }
</script>
<script async defer
src="https://maps.googleapis.com/maps/api/js?key=<myKey>&callback=initMap">
</script>

So I know that in order to open the page itself all I need to do is:

System.Diagnostics.Process.Start(pathToHtmlFile);

but how exactly can I pass the locations I find in the WPF side to the JS initMap function?

Use GET semantics in the file open command.

You must use a URI as the command: "file://pathToHtmlFile?lat=" + Lat + "&lng=" + Long"

Add a function to the script block:

function get(name){
   if(name=(new RegExp('[?&]'+encodeURIComponent(name)+'=([^&]*)')).exec(location.search))
      return decodeURIComponent(name[1]);

and call the get function for lat and lng.

A different approach is to build the html file on the fly. This supports an arbitrarily large number of markers (Maximum of 256 to ~5000+, depending on memory and API constraints).

Split the HTML file into two parts, top and bottom (see the code below), on where the data needs to be inserted between.

  1. Load the "top part" from the file into the variable Output and append the center of the map with the maps closing data tag } ); .

  2. Then loop through each marker, adding a new marker Javascript variable marker0 , marker1 ... for each defined marker. This code is then appended to the Output variable.

  3. The "bottom part" is appended to the Output variable and saved to the output file. This "end" file closes the opening function initMap(){ with } and the script tag and includes the load script for the Google Map with callback.

  4. This saved file is then opened by the browser(no data passing required).

Place the top of the html file in a new project file called topTemplate.html (set the Properties: compile options to none and Copy to output directory: copy if newer):

<script>
  function initMap() {

    let map = new google.maps.Map(document.getElementById('map'), {
      zoom: 4,
      center:     

Note: I removed the let myLatLng = {lat: -25.363, lng: 131.044}; from the code, I "hard-coded"(to be defined in C#) it to simplify the division of segments(2 vs 3).

And the last portion in bottomTemplate.html (Same property settings as the above file):

    }
</script>
<script async defer
src="https://maps.googleapis.com/maps/api/js?key=<myKey>&callback=initMap">
</script>

Then in the WPF application:

To make things easier, I define a struct to hold the markers with a helper function to build the Output string.

public struct Marker
{
    public float Lat;
    public float Lng;
    public string Title;

    public Marker(float lat,float lng, string title)
    {
        Lat = lat;
        Lng = lng;
        Title = title;
    }
    
    public string BuildHTML(int nameAppend)
    {
        return  String.Format("\n let marker{0} = new google.maps.Marker({{ position: {{lat: {1:###0.0####}, lng: {2:###0.0####}}}, map: map, title: '{3}'}}); \n", nameAppend, Lat, Lng, Title);
    }
}

The {{ and }} are the literal form of {} . The ###0.0### returns an actual number to four decimal places. Without this code, the float could be represented by a scientific notation value or localized to a "," separator, either of which may be incompatible with the API. The "#"'s can be extended to the right of the decimal point to increase precision if needed.

The following code is placed mostly in the Method that opens the file, see comments:

// at top of file add:
using System.IO;
// rest of namespace, class definition and method opening

// Assumes all of the markers are stored in: List<Markers> markers = new List<Markers>();
// and the center of the map is stored in float lat; and float lng;

string Output = File.ReadAllText("topTemplate.html");
// Since the topTemplate ended with "center: " we must fill in the values and close the request.
Output += String.Format("{{lat: {1:###0.0####}, lng: {2:###0.0####}}} }} );\n",lat, lng);
int index = 0;
foreach(var item in markers)
{
    Output += item.BuildHTML(index++); //adds 1+ to the marker0 to form another variable marker1 ... for each marker variable name.
}
Output += "\n"; //add a vertical space to the Output. Before adding the end:
Output += File.ReadAllText("bottomTemplate.html");

 // we will assume pathToHtmlFile contains a valid filename and is in a writable directory. If not, move the path to %ProgramData% or %temp%.

File.WriteAllText(pathToHtmlFile, Output); // will overwrite the file if it exists.
// start the browser. 
System.Diagnostics.Process.Start(pathToHtmlFile);

// The remainder of the file(method remainder and close, class remainder and close...)

The starting of the browser may not run on client computers since, "This member(Process.Start) cannot be used by partially trusted code." This is a side note to the actual answer. Be sure to use an installer.

The indentation will be incorrect in the generated file, since Javascript is whitespace independent(all whitepace(newlines, tabs and multiple spaces are reduced to a single space)), this is not an issue.

The list of markers must be declared( List markers = new List();) as either a class level variable or in the top of the method.

Use markers.Add(new Marker(Latitude, Longitude, Title)); where "Latitude, Longitude, and Title" are replaced with their respective values(or variables containing them), to add each of the markers; Prior to calling the "method" above.

This is example for leaflet(leaflet.html) and WPF

In MainWindow.xaml:

       public partial class MainWindow : Window
{
    private readonly WebSocketServer _wssv;

    public static Echo Echo;

    public MainWindow()
    {
        InitializeComponent();

        Closing += MainWindow_Closing;

        _wssv = new WebSocketServer("ws://127.0.0.1:7890");
        _wssv.AddWebSocketService<Echo>("/Echo");
        _wssv.Start();

        SubscibeEchoAsync();
    }

    private void MainWindow_Closing(object sender, System.ComponentModel.CancelEventArgs e)
    {
        if (Echo != null)
            Echo.OnMessageReceive -= Echo_OnMessageReceive;

        StopWebSocket();
    }

    private void StopWebSocket()
    {
        if (_wssv.IsListening)
            _wssv.Stop();
    }

    private void SubscibeEchoAsync()
    {
        Task.Run(async () => {
            while (Echo == null)
            {
                await Task.Delay(100);
            }

            Debug.Print("Map is initialized.");

            Echo.OnMessageReceive += Echo_OnMessageReceive;
        });
    }

    private void Echo_OnMessageReceive(string msg)
    {
        Debug.Print(msg);
    }

} }

In leaflet.html:

var socket = new WebSocket("ws://127.0.0.1:7890/Echo");

    socket.onopen = function () {
        alert("Connection is made.");
        socket.send("init");
    };

    socket.onclose = function (event) {
        if (event.wasClean) {
            alert('Connection is closed');
        } else {
            alert('Connection stopped');
        }
        alert('Code: ' + event.code + ' cause: ' + event.reason);
    };

socket.onmessage = function (event) { //alert( C# says: ${event.data} );

        if (event.data == "polygon") {
            var  = L.circle([53.931, 27.65], {
                color: 'red',
                fillColor: '#f03',
                fillOpacity: 0.5,
                radius: 500
            }).addTo(map);
            circle1 = L.circle([53.931, 27.65], 1000).addTo(map);

            return;
        }

}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM