MapPoint Programming: A Simple Add-in for MapPoint API Beginners

This article is based on a previous article published in the MP2K Magazine.

This How To article is prompted by AgDawg's post on the MapPoint forums. It is intended to show how to create a simple add-in that uses Visual Basic 6 (VB6) - a part of Visual Studio 6. It also demonstrates pushpin processing and simple shape drawing. Namely, it draws circles of 30 miles radius around every pushpin in the My Pushpins pushpin set.

VB6 might seem an odd choice for 2008. It is old. It is creaky. Microsoft have recently withdrawn their support. Despite this, much of the MapPoint programming documentation still uses it. It is also easy to use with MapPoint's COM interface. C# might be a much nicer language with wonderful .NET bells and whistles, but writing a MapPoint add-in using C# is always much more work than it should be. So although serious developers should probably look at C++ or C#, VB6 is a great entry point to learning MapPoint's API and object model. It is also cheap - Visual Studio is sufficiently out of date that sealed unopened copies are no longer in demand.

Anyway, let's get started!

Start Visual Basic 6 and in the New/Open project wizard, select the New tab and the Addin.

By default, VB6 will save your project in the main VB98 directory. This is a very bad place, and will probably cause an error on Windows Vista. It is recommended that you save your project to a better location immediately - before you modify your project. It is recommended that you save the add-in to a new directory in your My Documents directory.


After saving your project, go to the Project menu and select References. This will display the dialog box on the right:

Scroll down to Microsoft MapPoint Object Library, check the check box and press OK. The exact name will vary according to the version used. In this example, 13.0 refers to MapPoint 2006.


Set the name of the project using the Project Properties dialog box (select Properties on the Project menu).


A project browser should be visible on the right. Select the Connect.Dsr file that is listed under Designers. Use this to set the name, and to tell VB6 that the Addin will be for MapPoint.

VB6 has also added an empty form. We do not need this dialog box. Select it in the project browser, press the delete key.

If you right click on the Connect.Dsr file, you can see the VB6 code that has been automatically created for you. This should connect MapPoint to your add-in. Unfortunately VB6 assumed you were writing an add-in for Visual Studio, so we need to make some changes. It is considered good programming practice to separate code into logical chunks, so we shall create a separate module to do the actual circle drawing. Here is the modified connection code:


' Always use this line: It tells the compiler to check that names are spelt correctly
' and declared properly. This will catch a lot of typing errors which can be surprisingly
' difficult to find at runtime
Option Explicit

' This is our reference to the parent MapPoint Application
Public oApp  As MapPoint.Application


' This event fires when the menu item is clicked

Public Sub AddinInstance_RunDemo()
' ByVal CommandBarControl As Object, handled As Boolean, CancelDefault As Boolean)
    ' Call our circle drawing code
    Call DrawCircles(oApp)
End Sub


' MapPoint calls this method when it loads the AddIn and first connects to it
' We add the menu item here

Private Sub AddinInstance_OnConnection(ByVal Application As Object, ByVal ConnectMode As AddInDesignerObjects.ext_ConnectMode, ByVal AddInInst As Object, custom() As Variant)
    ' Display an error and finish, if we have an error
    On Error GoTo error_handler
    
    ' Keep a reference to the MapPoint object
    ' This will then be available when we need it
    Set oApp = Application
    
    ' Add the menu item. Menu text will say "Circle Demo..."
    ' Clicking the menu item calls the RunDemo method on this object (defined above)
    oApp.AddCommand "Circle Demo...", "AddinInstance_RunDemo", Me
    
    ' Done!
    Exit Sub
    
error_handler:
    ' All errors jump to here
    ' Display the error in a simple dialog box
    MsgBox Err.Description
End Sub


' This method removes the add-in from MapPoint
' It is called when MapPoint is closing and/or is disconnecting the add-in

Private Sub AddinInstance_OnDisconnection(ByVal RemoveMode As AddInDesignerObjects.ext_DisconnectMode, custom() As Variant)
    ' Keep going if we have an error
    On Error Resume Next
    
    ' Remove all menu items and other call-backs
    oApp.RemoveCommands Me
        
    ' Good hygene, but probably not necessary
    Set oApp = Nothing
End Sub

DrawCircles() is a new subroutine which we need to write. This is the 'guts' of the add-in and will go in the new module. Create a new module by right clicking on the project name in the project browser, and select Add followed by Module from the menus. Name the module MapCode and save it.

The DrawCircles() subroutine definition and initialization are as follows:

Public Sub DrawCircles(oMap As MapPoint.Application)
    On Error GoTo DrawCircles_Error

     ' It is convenient to create some references here
    Dim oMap As MapPoint.Map
    Set oMap = oApp.ActiveMap   ' our map object
    
    Dim oDS As MapPoint.DataSet ' Used to reference the current dataset
    Dim oRS As MapPoint.Recordset ' Used to process the pushpins
    Dim oPin As MapPoint.Pushpin  ' Reference to the current pushpin
    Dim oLoc As MapPoint.Location ' Location for the new circle
    Dim oShp As MapPoint.Shape    ' Reference to the new shape
       
    ' Probably the default but set it to be sure
    oApp.Units = geoMiles
    
    ' INSERT SOMETHING INTERESTING HERE (ie. processing code)


Finished_Processing:
    ' Tidy up
    ' It is a good idea to do this so that MapPoint/Windows/VB does not think objects
    ' are being used when they are not. This allows memory to be freed and avoids
    ' MapPont hanging around in memory
    Set oDS = Nothing
    Set oMap = Nothing
    Set oRS = Nothing
    Set oPin = Nothing
    Set oLoc = Nothing
    Set oShp = Nothing

    Exit Sub
    
DrawCircles_Error:
    MsgBox "Error: " & Err.Description
    GoTo Finished_Processing
End Sub

As with the connection method, all errors result in a dialog box and stop processing. We also define a few MapPoint reference variables which we shall be using. These are all reset to nothing at the end. This is good practice with all MapPoint programming. The system is clever and de-allocates memory when there are no longer any references to it. Unfortunately the removal of these references is complicated by COM, and we have to explicitly clear all references that we use. If we do not, then some of these objects might appear to be in use when MapPoint comes to close. MapPoint will not remove the objects, and so it will remain in memory as a 'zombie'.

MapPoint treats pushpin sets as simply a special kind of dataset. We want to work with the 'My Pushpins' pushpin set. Here we do this by looping over all datasets. Note that we could easily change this to work with all pushpin sets, or specific combinations.

    ' Loop over each dataset
    For Each oDS In oMap.DataSets
    
        ' Is the dataset a pushpinset or a multi-pushpin set?
        If (oDS.DataMapType = geoDataMapTypePushpin Or oDS.DataMapType = geoDataMapTypeMultipleSymbol) Then
            
            ' We have a pushpin set!
            ' Is it called "My Pushpins"?
            If oDS.Name = "My Pushpins" Then
                ' Yes this is the pushpin set we want - so query all the pushpins
                
' loop over all pushpins, here

                ' As there can be only one pushpin set with this name, we might as well exit
                GoTo Finished_Processing
            End If
        End If
    
        ' Loop around to get the next dataset
    Next

Yes it is that simple - one loop that goes over all datasets, and then a couple of if statements to find the pushpin set with a specific name. Note that we exit when we have found (and processed) the required pushpin set.

Records and pushpins in a dataset are processed through a query. We can query on, for example, all pushpins within a shape. We want all pushpins, so we use the QueryAllRecords method. This returns a RecordSet object. We simply loop over this to find each pushpin. Note that some pushpin records may not have been located - e.g. the data import wizard could not locate them. We need to filter these out, otherwise we shall get errors.

                ' Fetch a recordset of all pushpins in this dataset
                Set oRS = oDS.QueryAllRecords

                ' Move to the first record/pushpin
                Call oRS.MoveFirst
                
                ' Loop over all records/pushpins
                Do Until oRS.EOF()
                
                    ' Check that this record is matched to a location on the map
                    If oRS.IsMatched Then

                        Set oPin = oRS.Pushpin

                        ' Do something interesting with the pushpin, here                    
                    
                    End If
                
                    ' Move to the next record and repeat
                    Call oRS.MoveNext
                Loop

Again we have a simple loop. This time we loop over all data records, finding valid pushpins. To finish the add-in, all we need is the pushpin processing code. We need to fetch the pushpin's location and then use it to create a shape - a 30mile diameter circle. This is as follows:

                        ' Get the pushpin and its location
                        Set oPin = oRS.Pushpin
                        Set oLoc = oPin.Location
                        
                        ' Create the shape: Circle of 30 miles radius, size is not displayed
                        Set oShp = oMap.Shapes.AddShape(geoShapeOval, oLoc, 30#, 30#)
                        oShp.SizeVisible = False

The final MapCode module looks like this:

' Good practice, as before
Option Explicit

Public Sub DrawCircles(oApp As MapPoint.Application)
    On Error GoTo DrawCircles_Error
    
    ' It is convenient to create some references here
    Dim oMap As MapPoint.Map
    Set oMap = oApp.ActiveMap   ' our map object
    
    Dim oDS As MapPoint.DataSet ' Used to reference the current dataset
    Dim oRS As MapPoint.Recordset ' Used to process the pushpins
    Dim oPin As MapPoint.Pushpin  ' Reference to the current pushpin
    Dim oLoc As MapPoint.Location ' Location for the new circle
    Dim oShp As MapPoint.Shape    ' Reference to the new shape
    
       
    ' Probably the default but set it to be sure
    oApp.Units = geoMiles
    
    ' Loop over each dataset
    For Each oDS In oMap.DataSets
    
        ' Is the dataset a pushpinset or a multi-pushpin set?
        If (oDS.DataMapType = geoDataMapTypePushpin Or oDS.DataMapType = geoDataMapTypeMultipleSymbol) Then
            
            ' We have a pushpin set!
            ' Is it called "My Pushpins"?
            If oDS.Name = "My Pushpins" Then
                ' Yes this is the pushpin set we want - so query all the pushpins
                
                ' Fetch a recordset of all pushpins in this dataset
                Set oRS = oDS.QueryAllRecords

                ' Move to the first record/pushpin
                Call oRS.MoveFirst
                
                ' Loop over all records/pushpins
                Do Until oRS.EOF()
                
                    ' Check that this record is matched to a location on the map
                    If oRS.IsMatched Then
                    
                        ' Get the pushpin and its location
                        Set oPin = oRS.Pushpin
                        Set oLoc = oPin.Location
                        
                        ' Create the shape: Circle of 30 miles radius, size is not displayed
                        Set oShp = oMap.Shapes.AddShape(geoShapeOval, oLoc, 30#, 30#)
                        oShp.SizeVisible = False
                    
                    End If
                
                    ' Move to the next record and repeat
                    Call oRS.MoveNext
                Loop
            
                ' All pushpins have been processed
                ' As there can be only one pushpinset with this name, we might as well exit
                GoTo Finished_Processing
            End If
        End If
    
        ' Loop around to get the next dataset
    Next
    
Finished_Processing:
    ' Tidy up
    ' It is a good idea to do this so that MapPoint/Windows/VB does not think objects
    ' are being used when they are not. This allows memory to be freed and avoids
    ' MapPont hanging around in memory
    Set oDS = Nothing
    Set oMap = Nothing
    Set oRS = Nothing
    Set oPin = Nothing
    Set oLoc = Nothing
    Set oShp = Nothing
    
    Exit Sub
    
DrawCircles_Error:
    MsgBox "Error: " & Err.Description
    GoTo Finished_Processing
End Sub
' Good practice, as before
Option Explicit

Public Sub DrawCircles(oApp As MapPoint.Application)
    On Error GoTo DrawCircles_Error
    
    ' It is convenient to create some references here
    Dim oMap As MapPoint.Map
    Set oMap = oApp.ActiveMap   ' our map object
    
    Dim oDS As MapPoint.DataSet ' Used to reference the current dataset
    Dim oRS As MapPoint.Recordset ' Used to process the pushpins
    Dim oPin As MapPoint.Pushpin  ' Reference to the current pushpin
    Dim oLoc As MapPoint.Location ' Location for the new circle
    Dim oShp As MapPoint.Shape    ' Reference to the new shape
    
       
    ' Probably the default but set it to be sure
    oApp.Units = geoMiles
    
    ' Loop over each dataset
    For Each oDS In oMap.DataSets
    
        ' Is the dataset a pushpinset or a multi-pushpin set?
        If (oDS.DataMapType = geoDataMapTypePushpin Or oDS.DataMapType = geoDataMapTypeMultipleSymbol) Then
            
            ' We have a pushpin set!
            ' Is it called "My Pushpins"?
            If oDS.Name = "My Pushpins" Then
                ' Yes this is the pushpin set we want - so query all the pushpins
                
                ' Fetch a recordset of all pushpins in this dataset
                Set oRS = oDS.QueryAllRecords

                ' Move to the first record/pushpin
                Call oRS.MoveFirst
                
                ' Loop over all records/pushpins
                Do Until oRS.EOF()
                
                    ' Check that this record is matched to a location on the map
                    If oRS.IsMatched Then
                    
                        ' Get the pushpin and its location
                        Set oPin = oRS.Pushpin
                        Set oLoc = oPin.Location
                        
                        ' Create the shape: Circle of 30 miles radius, size is not displayed
                        Set oShp = oMap.Shapes.AddShape(geoShapeOval, oLoc, 30#, 30#)
                        oShp.SizeVisible = False
                    
                    End If
                
                    ' Move to the next record and repeat
                    Call oRS.MoveNext
                Loop
            
                ' All pushpins have been processed
                ' As there can be only one pushpinset with this name, we might as well exit
                GoTo Finished_Processing
            End If
        End If
    
        ' Loop around to get the next dataset
    Next
    
Finished_Processing:
    ' Tidy up
    ' It is a good idea to do this so that MapPoint/Windows/VB does not think objects
    ' are being used when they are not. This allows memory to be freed and avoids
    ' MapPont hanging around in memory
    Set oDS = Nothing
    Set oMap = Nothing
    Set oRS = Nothing
    Set oPin = Nothing
    Set oLoc = Nothing
    Set oShp = Nothing
    
    Exit Sub
    
DrawCircles_Error:
    MsgBox "Error: " & Err.Description
    GoTo Finished_Processing
End Sub

Visual Basic will automatically install this add-in when you build it. To distribute it, you will need to distribute the resulting DLL library. This then has to be manually added to MapPoint using the COM Add-Ins... entry on the Tools menu. You may also have to distribute some of the VB6 DLLs, although these will already have been installed on many PCs. See your VB6 documentation as to which DLLs you need to distribute - they will depend on which facilities you intend to use.


Return to the Main MapPoint Articles Page.