Using Bing Maps in Blazor with JSInterop and TypeScriptLouis HendricksBlockedUnblockFollowFollowingMay 19In this article we are going to work with the Bing Maps web control in a client side Blazor application.
As there is not a Blazor native Bing Maps control we will be using TypeScript to access the Bing Maps API.
In order to communicate with TypeScript from C# we will be making use of JSRuntime calls in Blazor.
Getting startedAll the work in this article is being done with Visual Studio 2019 Community Edition 16.
1 Preview 3.
Everything will work with .
Net Core 3 Preview 4 or Preview 5.
Start a new project using the Blazor (ASP.
Net Core Hosted) template.
To use this you will also need your own Bing Maps API key.
You can sign up for your own free key at https://www.
I am doing all of my client side Blazor development using MVVM.
That is not part of this article, but if you are wondering why I’m creating folders in my project named Views and Models that is the reason.
If you are interested in reading my Blazor MVVM articles the first article is linked below.
A simple MVVM implementation in client side Blazor.
This article is the first one in a series where I will cover how I currently plan to create a Blazor client side…itnext.
To make it accessible to the TypeScript we will be writing we are going to add a <script> tag to the index.
html in our wwwroot folder of our client project.
com/api/maps/mapcontrol?key=MyBingKey' ></script>Make sure you edit the above line to replace MyBingKey with your actual key you received when you registered.
With that in place the Bing Maps API will now be available to your code.
Setting up our Blazor componentBing Maps requires the name of a <div> to initialize itself.
To make this available to Bing we are going to add a new component to the Blazor sample template and put the needed <div> in it.
For my project I made a new folder and named it Views and inside of that folder I added a new Razor View and named it SiteMapper.
You can use whatever naming you are comfortable with, mine is specific to a mapping project that I am working on.
Inside of your new component enter the following code:For some reason setting height to 100% doesn’t work but using the viewport height relative length does.
We now have a <div> with the id myMap which Bing Maps can use.
After our component is created we need to go add it to NavMenu.
razor in the Pages folder to make it appear when we launch our application.
We will also need to make a copy of _Imports.
razor and place it into our Views folder.
Setting up TypeScriptBlazor will already compile TypeScript that is added to the built in client project out of the box; we only need to set up our folder structure.
We are also going to add the Bing Map 8 TypeScript definition files to our project to give us Intellisense for Bing Maps when we are editing our TypeScript.
Underneath our wwwroot folder add a new folder and name it ts.
To get our definitions for Intellisense go here and down load the definition files:microsoft/Bing-Maps-V8-TypeScript-DefinitionsThis project contains the TypeScript definitions for the Bing Maps V8 Web Control.
comMake a new folder under your ts folder and name it types.
Copy everything from below the types folder in the definitions you just downloaded into that folder.
Also make an interfaces folder below your ts folder which we will use a little bit later.
Your folder structure under TS will look like this:With our folders set up and our definitions in place we can add our first TypeScript file.
In the ts folder add a new TypeScript file and name it BingTsInterop.
Inside of that TypeScript file add the following content:The first line is a reference which is similar to a using statement in C#.
It is telling our TypeScript about the definition files we have available and providing us with IntelliSense.
Line 3 is declaring a single instance of our BingMap class which is defined in lines 5 though 15.
The class contains an instance of a Map and a constructor which creates a new map which will be placed in the myMap div.
The constructor also specifies some MapLoadOptions, in this case the center of the map, the map type and the zoom level.
The loadMap()function on line 17 through 19 assigns a new instance of our BingMap class to bingMap.
This is the function we want to call from JSInterop.
Calling loadMap() from BlazorIf we do a build now we will see that BingTSInterop.
ts has been compiled to create BingTSInterop.
Now that we have our output go back into index.
html and make it available to our application with this tag:<script src=".
js"></script>To add the needed C# to our component lets add a code behind file.
In the Views folder add a new C# class and name it SiteMapper.
Change the class’ name to SiteMapperBase and have it inherit from ComponentBase.
To make a call to TypeScript we will need to also add using MicrosoftJSInterop; to our using statements.
Our class will look like this:The class is pretty simple so far.
We have one property, JSRuntime which has been injected to our class and we have are overriding OnAfterRenderAsync() to make our call to loadMap.
Because there is no return value from loadMap we are using InvokeAsync<Task>.
If there had been a return value we would need an appropriate type specified.
With our code behind set up we can go back into SiteMapper.
razor and add the following line just below the @page directive.
@inherits SiteMapperBaseWith that in place our code behind will now be part our View without needing to keep everything in a single file.
At this point we can launch our application.
As soon as our component finishes rendering we now call our TypeScript loadMap function.
We can change the view or settings of the map then navigate to a different control and come back and loadMap will be called again and set it back to the default values.
Adding a markerNow that we have a map let’s do something with it.
A pretty common task with maps is to place a pushpin on it.
There is a very large repository of Bing Maps samples at https://bingmapsv8samples.
I’m a fan of the SVG Template Pushpin (it looks clean and the color is easy to set) so let’s look at that code:There’s a bit of code but most of it is static code to making the pin.
The main points of interest to us are the call to map.
getCenter() on the first line which determines the location of the pin in that code sample and then on line 10 where the color gets set to blue.
If we can pass in the location and color we will have a reusable pushpin.
Our goal is to wire up all of ourTypeScript in a way that we can forget that it is there and focus on doing the real work in Blazor.
In order to do that we need a reliable way to pass data in to TypeScript.
We can see by looking at the code that color is a string but what is map.
getCenter()?.If we type that function into our TypeScript file we can mouse hover over it and see that it returns a Microsoft.
We’re already using that type in the BingMap constructor.
If we mouse hover over that type we can see constructor Microsoft.
Location(latitude:any, longitude:any) so it looks like we need to pass a color, latitude and longitude into TypeScript to generate a custom pushpin.
How do we send all 3 values over using JSInterop?.We could send them as 3 objects using args but I have a feeling that will make our code harder to read.
It would be better if they were all members of one object.
TypeScript InterfacesTypeScript, just like C#, allows us to write interfaces to use as contracts in our code.
We are going to create a Typescript interface for our input and then create the same data structure in C# and pass it in from Blazor.
If everything works as expected we’ll be able to create our own object in Blazor and pass it directly to TypeScript.
Go to the wwwroot/ts/interfaces folder we created earlier and create a new TypeScript file in it and name it BingMapsInterfaces.
We decided we wanted a string and a location earlier so put this code into the file:This is the syntax for a TypeScript interface.
We first declare an interface and name it and then declare the members.
Except for the typing syntax it is really close to C#.
In order to make this interface available to our code we will add a new reference to BingTsInterop.
/// <reference path="interfaces/BingMapsInterfaces.
ts" />That statement makes anything we create in BingMapsInterfaces.
ts available to our code.
Next we will go back to our Blazor code and create what we hope will be a matching class.
Make a Models folder in the client project and then create a new C# class and name it BingMapObjects.
First we will create our Location class.
In Typescript we had number for latitude and longitude, here we will use double.
We will also add a constructor to make our code cleaner when we are assigning values.
With the Location defined we can now make a Pushpin class.
This will simply be the color and the location.
If we wired up everything correctly we will be able to add a pushpin to our map from Blazor now.
Go back into SiteMapper.
cs and add this code to our OnAfterRenderAsync method:Now create the AddPushpin function in BingTsInterop.
ts:We have created a TypeScript function which accepts a BingPin and has no return value.
We can see that we are passing in the location as well as the color from sitePin which we have passed in from our C# code.
The second statement of the function simply places the pushpin we created onto the map.
If we launch our application again we should now see a green pushpin.
Adding a PolygonAdding a polygon to the map is another common task.
If we look at the code sample for a basic polygon on the github repository it doesn’t look too complicated:The code gets the center of the map the uses two additional points to define a triangle.
These points are put into exteriorRing.
Each polygon is made up of one or more rings.
The first ring is the exterior ring which defines the polygon and it’s fill area.
Each ring after the first one is an interior ring which defines areas inside of the polygon which become holes in the polygon.
The actual constructor comes next and it doesn’t look to bad.
First is the exterior ring, then a string for the fill color, a string for the stroke color and a number for the stroke thickness.
It shouldn’t be too difficult to define the interface and class to pass this information in to Typescript.
Temporarily adding a polygon constructor to our typescript code and mouse hovering over it shows something interesting.
Location | Microsoft.
Location, options?: Microsoft.
IPolygonOptions)It looks like the first argument can either be a single ring or an array of rings (a ring is a Location).
Also the options parameter is not required but we will supply it because we want to make use of it.
If we are going to define a single interface which will allow one or multiple rings we will have to define it to be Microsoft.
Location even if we are only passing a single ring.
Go back into BingMapsInterfaces.
ts and add this code to define an interface for our options:That was just a straight list of the three options we want to provide.
Next we can define the actual polygon object we want to pass in from Blazor:As discussed earlier we are always going to use the syntax for multiple rings even if we are only passing a single ring.
With the interfaces defined this way the function to add our polygon to the map becomes pretty short.
Go back into BingTsInterop.
ts and add this function:We just call the constructor and push it to the map and we are done.
Making a circleSome business requirements necessitated coming up with code that could create a circle roughly 10 miles in diameter centered on a specified point.
Instead of picking points by hand we are going to use that code in this article.
Create a new class MapCircle.
cs in the Models folder and paste in this code:That code uses math to make a circle out of points around a center point.
The loop near the bottom uses 3 as the interval so that a circle is only made of 120 points instead of 360 points.
That number can be raised or lowered as needed for resolution or performance.
We now have to go back to BingMapsObjects.
cs and add the classes in there to match our TypeScript interfaces.
Looking at the Typescript our C# classes wind up looking like this:It’s a 1 for 1 mapping and should work when we make the JSInterop call.
All that’s left now is to add the code to create our Polygon to SiteMapper.
Here is all of the needed code:This code first declares a new Polygon then declares a List<Location> for the rings.
List<T> is easier to work with and has the handy .
In line 3 we call DrawMapCircle and add the circle as our first (the exterior) ring.
In lines 4 through 6 we draw a smaller circle and reverse its direction then add it as the second ring.
If the interior ring does not go in the opposite direction of the exterior ring it will not render properly.
Line 7 turns our List of rings into an Array.
The rest of the code instantiates the PolygonOptions and sets the values we want to pass in.
Finally line 13 calls the AddPolygon method we created in TypeScript.
If we run our application now we should see a donut sort of shape on our map.
Wrapping UpIn this article we took our first look at using the Bing Maps 8 web control in a client side Blazor application.
We started off with getting an API key and making the API available to our code.
We followed that up by setting up our TypeScript folders and adding the Bing Map type definitions to give us Intellisense for Bing in Typescript.
Finally we went through how to initialized the map and add pushpins and polygons to it.
In that process we also covered using TypeScript interfaces to cleanly pass data from C# to Typescript.
There is a lot more to the Bing Maps 8 API and we have just scratched the surface.
Next time we may cover addtional functions of the API or we may dive into the Bing Maps REST API.
If you have questions or comments you can leave them here or reach out to me @LouisHendricks on Twitter.
.. More details