2

I want to write a Windows Forms application in VB 2010 that will allow the user to search and view addresses or coordinate points on Google Maps. I've already done this by using a WebBrowser item. However I want to use a wrapper so that I can just display the map to the user, but still be able to move and zoom the map or give directions, etc. I know there are ways to do this when developing an ASP.NET site, but I want to do this for a WindowsForms app. Could anyone please help?

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
user1145581
  • 1,017
  • 4
  • 13
  • 18

5 Answers5

7

Probably too late, but I recently had to deal with google maps in vb and wanted to share my solution:

The first part of this example explains how to implement it. In the second, I will explain how it works. This tries to be a general example. The template for the map (see step 3) and the example functions are fully customizable.

################################# IMPLEMENTATION ############################

Step 1. Firstly, create a new project and select Windows Form Application. Let's leave its name as "Form1".

enter image description here

Step 2. Add a WebBrowser control (which will hold your map) to your Form1. Let's call it "wbmap"

Step 3. Create a .html file named "googlemap_template.html" with your favourite text editor and paste the following code:

googlemap_template.html

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge"/>
     <style type="text/css">
      html, body {
        height: 100%;
        margin: 0;
        padding: 0;
      }
      #gmap {
        height: 100%;
      }
     </style>
    <script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=false"></script>
    <script type="text/javascript">
        function initialize() {
            //Use window.X instead of var X to make a variable globally available 
            window.markers = new Array();
            window.marker_data = [[MARKER_DATA]];
            window.gmap = new google.maps.Map(document.getElementById('gmap'), {
            zoom: 15,
            center: new google.maps.LatLng(marker_data[0][0], marker_data[0][1]),
            mapTypeId: google.maps.MapTypeId.ROADMAP
          });
          var infowindow = new google.maps.InfoWindow();
          var newmarker, i;
          for (i = 0; i < marker_data.length; i++) {
              if (marker_data[0].length == 2) {
                  newmarker = new google.maps.Marker({
                      position: new google.maps.LatLng(marker_data[i][0], marker_data[i][1]),
                      map: gmap
                  });
              } else if (marker_data[0].length == 3) {
                  newmarker = new google.maps.Marker({
                      position: new google.maps.LatLng(marker_data[i][0], marker_data[i][1]),
                      map: gmap,
                      title: (marker_data[i][2])
                  });
              } else {
                  newmarker = new google.maps.Marker({
                      position: new google.maps.LatLng(marker_data[i][0], marker_data[i][1]),
                      map: gmap,
                      title: (marker_data[i][2]),
                      icon: (marker_data[i][3])
                  });
              }
            google.maps.event.addListener(newmarker, 'click', (function (newmarker, i) {
                return function () {
                    if (newmarker.title) {
                        infowindow.setContent(newmarker.title);
                        infowindow.open(gmap, newmarker);
                    }
                    gmap.setCenter(newmarker.getPosition());
                    // Calling functions written in the WF
                    window.external.showVbHelloWorld();
                    window.external.getMarkerDataFromJavascript(newmarker.title,i);
                }
            })(newmarker, i));
            markers[i] = newmarker;
          }
        }
        google.maps.event.addDomListener(window, 'load', initialize);
    </script>
    <script type="text/javascript">
        // Function triggered from the WF with no arguments
        function showJavascriptHelloWorld() {
            alert("Hello world in HTML from WF");
        }
     </script>
      <script type="text/javascript">
        // Function triggered from the WF with a String argument
        function focusMarkerFromIdx(idx) {
            google.maps.event.trigger(markers[idx], 'click');
        }
      </script>
  </head>
  <body>
    <div id="gmap"></div>
  </body>
</html>

This will serve as our map template. I will explain how it works later.

Step 4. Add the googlemap_template.hmtl file to your project (right click on your project->add->existing item)

Step 5. Once it appears in your Solution Explorer, set its properties to: - Build Action -> Embedded Resource - Custom Tool Namespace -> write the name of the project

enter image description here

Step 6. Add a new class (right click on your project->add->class). In my example I'll call it GoogleMapHelper.

enter image description here

Step 7. Paste the following code into your class:

GoogleMapHelper.vb

    Imports System.IO
    Imports System.Reflection
    Imports System.Text

    Public Class GoogleMapHelper

    ' 1- googlemap_template.html must be copied in the main project folder
    ' 2- add the file into the Visual Studio Solution Explorer (add existing file)
    ' 3- set the properties of the file to: 
    '                                   Build Action -> Embedded Resource
    '                                   Custom Tool Namespace -> write the name of the project

    Private Const ICON_FOLDER As String = "marker_icons/" 'images must be stored in a folder inside  Debug/Release folder
    Private Const MAP_TEMPLATE As String = "WindowsApplication1.googlemap_template.html"
    Private Const TEXT_TO_REPLACE_MARKER_DATA As String = "[[MARKER_DATA]]"
    Private Const TMP_NAME As String = "tmp_map.html"


    Private mWebBrowser As WebBrowser

    'MARKER POSITIONS 
    Private mPositions As Double(,) 'lat, lon
    ' marker data allows different formats to include lat,long and optionally title and icon:
    ' op1: mMarkerData = New String(N-1, 1) {{lat1, lon1}, {lat2, lon2}, {latN, lonN}} 
    ' op2: mMarkerData = New String(N-1, 2) {{lat1, lon1,'title1'}, {lat2, lon2,'title2'}, {latN, lonN, 'titleN'}} 
    ' op3: mMarkerData = New String(N-1, 3) {{lat1, lon1,'title1','image1.png'}, {lat2, lon2,'title2','image2.png'}, {latN, lonN, 'titleN','imageN.png'}} 
    Private mMarkerData As String(,) = Nothing


    Public Sub New(ByRef wb As WebBrowser, pos As Double(,))
        mWebBrowser = wb
        mPositions = pos
        mMarkerData = getMarkerDataFromPositions(pos)
    End Sub

    Public Sub New(ByRef wb As WebBrowser, md As String(,))
        mWebBrowser = wb
        mMarkerData = md
    End Sub

    Public Sub loadMap()
        mWebBrowser.Navigate(getMapTemplate())
    End Sub

    Private Function getMapTemplate() As String

        If mMarkerData Is Nothing Or mMarkerData.GetLength(1) > 4 Then
            MessageBox.Show("Marker data has not the proper size. It must have 2, 3 o 4 columns")
            Return Nothing
        End If

        Dim htmlTemplate As New StringBuilder()
        Dim tmpFolder As String = Environment.GetEnvironmentVariable("TEMP")
        Dim dataSize As Integer = mMarkerData.GetLength(1) 'number of columns
        Dim mMarkerDataAsText As String = String.Empty
        Dim myresourcePath As String = My.Resources.ResourceManager.BaseName
        Dim myresourcefullPath As String = Path.GetFullPath(My.Resources.ResourceManager.BaseName)
        Dim localPath = myresourcefullPath.Replace(myresourcePath, "").Replace("\", "/") & ICON_FOLDER

        htmlTemplate.AppendLine(getStringFromResources(MAP_TEMPLATE))
        mMarkerDataAsText = "["

        For i As Integer = 0 To mMarkerData.GetLength(0) - 1
            If i <> 0 Then
                mMarkerDataAsText += ","
            End If
            If dataSize = 2 Then 'lat,lon
                mMarkerDataAsText += "[" & mMarkerData(i, 0) & "," + mMarkerData(i, 1) & "]"
            ElseIf dataSize = 3 Then 'lat,lon and title
                mMarkerDataAsText += "[" & mMarkerData(i, 0) & "," + mMarkerData(i, 1) & ",'" & mMarkerData(i, 2) & "']"
            ElseIf dataSize = 4 Then 'lat,lon,title and image
                mMarkerDataAsText += "[" & mMarkerData(i, 0) & "," + mMarkerData(i, 1) & ",'" & mMarkerData(i, 2) & "','" & localPath & mMarkerData(i, 3) & "']" 'Ojo a las comillas simples en las columnas 3 y 4 
            End If
        Next

        mMarkerDataAsText += "]"
        htmlTemplate.Replace(TEXT_TO_REPLACE_MARKER_DATA, mMarkerDataAsText)

        Dim tmpHtmlMapFile As String = (tmpFolder & Convert.ToString("\")) + TMP_NAME
        Dim existsMapFile As Boolean = False
        Try
            existsMapFile = createTxtFile(tmpHtmlMapFile, htmlTemplate)
        Catch ex As Exception
            MessageBox.Show("Error writing temporal file", "Writing Error", MessageBoxButtons.OK, MessageBoxIcon.[Error])
        End Try

        If existsMapFile Then
            Return tmpHtmlMapFile
        Else
            Return Nothing
        End If
    End Function

    Private Function getMarkerDataFromPositions(pos As Double(,)) As String(,)
        Dim md As String(,) = New String(pos.GetLength(0) - 1, 1) {}
        For i As Integer = 0 To pos.GetLength(0) - 1
            md(i, 0) = pos(i, 0).ToString("g", New System.Globalization.CultureInfo("en-US"))
            md(i, 1) = pos(i, 1).ToString("g", New System.Globalization.CultureInfo("en-US"))
        Next
        Return md
    End Function

    Private Function getStringFromResources(resourceName As String) As String
        Dim assem As Assembly = Me.[GetType]().Assembly

        Using stream As Stream = assem.GetManifestResourceStream(resourceName)
            Try
                Using reader As New StreamReader(stream)
                    Return reader.ReadToEnd()
                End Using
            Catch e As Exception
                Throw New Exception((Convert.ToString("Error de acceso al Recurso '") & resourceName) + "'" & vbCr & vbLf + e.ToString())
            End Try
        End Using
    End Function

    Private Function createTxtFile(mFile As String, content As StringBuilder) As Boolean
        Dim mPath As String = Path.GetDirectoryName(mFile)
        If Not Directory.Exists(mPath) Then
            Directory.CreateDirectory(mPath)
        End If
        If File.Exists(mFile) Then
            File.Delete(mFile)
        End If
        Dim sw As StreamWriter = File.CreateText(mFile)
        sw.Write(content.ToString())
        sw.Close()
        Return True
    End Function
    End Class

Note: The MAP_TEMPLATE constant must include the name of your project

Step 8. Now we can use our GoogleMapHelper class to load the map into our webbrowser by simply creating and instance and calling its loadMap() method. How you build your markerData is up to you. In this example, for clarification, I write them by hand. There are 3 options to define the marker data (see GoogleMapHelper class comments). Note that if you use the third option (including title and icons) you must create a folder called "marker_icons" (or whatever you define in the GoogleMapHelper constant ICON_FOLDER) in your Debug/Release folder and place there your .png files. In my case:

enter image description here

I created two buttons in my Form1 to illustrate how the map and the WF interact. Here is how it looks:

enter image description here

And here is the code:

Form1.vb

Imports System.IO
Imports System.Reflection
Imports System.Security.Permissions
Imports System.Text
<PermissionSet(SecurityAction.Demand, Name:="FullTrust")>
<System.Runtime.InteropServices.ComVisible(True)>
Public Class Form1

Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load

    Me.wbmap.ObjectForScripting = Me

    Dim onlyPositions As Double(,) = New Double(2, 1) {{42.13557, -0.40806}, {42.13684, -0.40884}, {42.13716, -0.40729}}
    Dim positonAndTitles As String(,) = New String(2, 2) {{"42.13557", "-0.40806", "marker0"}, {"42.13684", "-0.40884", "marker1"}, {"42.13716", "-0.40729", "marker2"}}
    Dim positonTitlesAndIcons As String(,) = New String(2, 3) {{"42.13557", "-0.40806", "marker0", "truck_red.png"}, {"42.13684", "-0.40884", "marker1", "truck_red.png"}, {"42.13716", "-0.40729", "marker2", "truck_red.png"}}

    'Dim gmh As GoogleMapHelper = New GoogleMapHelper(wbmap, onlyPositions)
    'Dim gmh As GoogleMapHelper = New GoogleMapHelper(wbmap, positonAndTitles)
    Dim gmh As GoogleMapHelper = New GoogleMapHelper(wbmap, positonTitlesAndIcons)
    gmh.loadMap()
End Sub

'############################### CALLING JAVASCRIPT METHODS ##############################
'This methods call methods written in googlemap_template.html
Private Sub callMapJavascript(sender As Object, e As EventArgs) Handles Button1.Click
    wbmap.Document.InvokeScript("showJavascriptHelloWorld")
End Sub

Private Sub callMapJavascriptWithArguments(sender As Object, e As EventArgs) Handles Button2.Click
    wbmap.Document.InvokeScript("focusMarkerFromIdx", New String() {2})
End Sub
'#########################################################################################

'############################### METHODS CALLED FROM JAVASCRIPT ##########################
'This methods are called by the javascript defined in googlemap_template.html when some events are triggered
Public Sub getMarkerDataFromJavascript(title As String, idx As String)
    MsgBox("Title: " & title & " idx: " & idx)
End Sub

Public Sub showVbHelloWorld()
    MsgBox("Hello world in WF from HTML")
End Sub
End Class

IMPORTANT : don't forget to add these lines before your class Form1 definition:

<PermissionSet(SecurityAction.Demand, Name:="FullTrust")>
<System.Runtime.InteropServices.ComVisible(True)>

What they do is to tell the .NET Framework that we want fulltrust and make the class visible to COM so Form1 is visible to JavaScript.

Also don't forget this in your Form1 load function:

Me.wbmap.ObjectForScripting = Me

It exposes your Form1 class to the JavaScript on the googlemap_template.hmtl page.

Now you can execute and it should be working

################################# HOW IT WORKS#################################

Basically, what our GoogleMapHelper class does is to read our googlemap_template.html, make a temporal copy, replace the code related to the markers ([[MARKER_DATA]]) and execute the page in the web browser control of our form. This html loops through all the markers and assigns a 'click' listener to each one. This click function is obviously fully customizable. In the example it opens an infowindow if the marker has a title, centers the map in such marker and calls two external functions that are defined in our Form1 class.

On the other hand, we can define other javascript functions (with or without arguments) in this html to be called from our Windows Form (by using wbmap.Document.InvokeScript).

Carlos Borau
  • 1,433
  • 1
  • 24
  • 34
  • Outstanding work here. I'm about to undergo a similar project. Hoping I can refer to this for guidance. I need an interface (w/menu) and a display that allows the user to click around the map to create a path (using this API): https://developers.google.com/maps/documentation/javascript/examples/polyline-complex#top_of_page. I THEN need a way to export their selection(s) to a KML file... Should be a blast... : / – user3108489 Jan 18 '17 at 19:50
  • glad you like it! it should indeed guide you. I haven't used polylines, but in any case they are just a list of marker positions, so you should not have problems to retrieve their lat,lon coordinates and call a function in your WF to export your file. Good luck! – Carlos Borau Jan 20 '17 at 08:37
  • Do you happen to have any updates for this awesome project you created? for example with supports for other things like marker clicks, drags etc – The Newbie Mar 19 '19 at 22:56
  • unfortunately this code is now embedded into a much wider one which I don't own. Marker clicks were already included though. And drag shouldn't be hard to implement (just refreshing the map with new marker positions while you hold mouse) – Carlos Borau Mar 20 '19 at 10:14
2

I have use this control and it's really fantastic. It let you use not only GoogleMaps but virtually all the major mapping services, however, the last time I have checked, they have got some licensing troubles with google.

GreatMaps on codeplex

Steve
  • 213,761
  • 22
  • 232
  • 286
0

Try this code to get direction between two location

Dim queryaddress As New System.Text.StringBuilder
        Dim sStreet As String = String.Empty
        Dim sCity As String = String.Empty
        Dim sState As String = String.Empty
        Dim sPincode As String = String.Empty
        Dim sProvider_no As String = String.Empty
        queryaddress.Append("https://www.google.com/maps/dir/")

        If txtprovider_no.Text <> "" Then
            sProvider_no = txtprovider_no.Text.Replace(" ", "+")
            queryaddress.Append(sProvider_no + "," & "+")
        End If
        If txtState.Text <> "" Then
            sState = txtState.Text.Replace("  ", "+")
            queryaddress.Append(sState + "," & "+")
        End If
        If txtCity.Text <> "" Then
            sCity = txtCity.Text.Replace("  ", "+")
            queryaddress.Append(sCity + "," & "+")
        End If
        If txtPincode.Text <> "" Then
            sPincode = txtPincode.Text.Replace("  ", "+")
            queryaddress.Append(sPincode)
        End If

        queryaddress.Append("/")
        sStreet = String.Empty
        sCity = String.Empty
        sState = String.Empty
        sPincode = String.Empty
        If txtlindmark.Text <> "" Then
            sStreet = txtlindmark.Text.Replace("  ", "+")
            queryaddress.Append(sStreet + "," & "+")
        End If
        If txtclient_city.Text <> "" Then
            sCity = txtclient_city.Text.Replace("  ", "+")
            queryaddress.Append(sCity + "," & "+")
        End If
        If ttxtclient_city.Text <> "" Then
            sPincode = ttxtclient_city.Text.Replace("  ", "+")
            queryaddress.Append(sPincode)
        End If
        If txtclient_state.Text <> "" Then
            sState = txtclient_state.Text.Replace("  ", "+")
            queryaddress.Append(sState + "," & "+")
        End If

        WBR.Navigate(queryaddress.ToString())
Tanmay Nehete
  • 2,138
  • 4
  • 31
  • 42
0

The Google Maps Control 2017 project wraps Google Maps API as ASP.NET Custom Control, providing an easy and fast way of adding Google Maps in your ASP.NET C# or VB pages with "Zero-Written-JavaScript" code.

ASP.NET Google Maps Control includes many features (markers, polyline, polygons, directions) and gives you almost all Google Maps API functionality in enjoyable ASP.NET way of coding.

PM>Install-Package GoogleMapControl

0

Google Maps API for .NET seeme to be what you are looking for

http://gmaps.codeplex.com/

from the site

This project intends to provide all the features available in the Google Maps API. It is being developed in C# for .NET Framework 3.5.

Although the project is in C# you can probably just add a reference to the project and use it just like any other reference and write your code in any CLR compliant languege , including VB.

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
Alex
  • 768
  • 1
  • 5
  • 13