Tip of the Day #23: Getting going with IIS Express

First, if you don’t have it already you need to download IIS Express (you can also use this link to get the full install, not via Microsoft’s web installer, if you are behind a proxy that is preventing the installation). And, I’d also recommend downloading Visual Studio 2010 SP1 and upgrading to it.

In your web project, open up the properties by right clicking the project and selecting properties, or pressing Alt+Enter while the project is selected.

You will then be presented with a view like this:

1 Initial Web Properties

By default, in the servers section of the Web tab the “Use Visual Studio Development Server” (aka Cassini) will be selected. Change this to “Use Local IIS Web Server”

2 Change to Local IIS Web Server

If you want to customise the settings you may do so. I tend to set a specific local port so that I know that all my applications don’t class with one another and that I can easily identify it later. My naming scheme to select 4 or 5 digits that are derived from the name of the project as if dialled into a telephone keypad. (Some people think that’s a bit weird but it makes it easy to avoid port clashes and to reverse the port into the project if you ever get lost.)

If necessary you can define the virtual directory in the Project URL and configure it by pressing “Create Virtual Directory”.

3 Setting a Virtual Directory

If you don’t “Create Virtual Directory” and you attempt to run the project, you’ll get a warning dialog that asks if you want to configure it. If you select yes, then Visual Studio will configure the virtual directory for you and start the application.

4 Not configuring a Virtual Directory

Finally, if you need to see what Sites IIS Express is running there is a tray icon you can right click on to see.

5 System Tray Icon for IIS Express

And if you click “Show all applications” you get to see all the sites that IIS Express is running. Clicking on a URL takes you to that site, anywhere else on the line will bring up details of the site in the lower part of the dialog.

6 IIS Express Running Applications

Clicking on the “Parent” name will take you to the instance of Visual Studio that the application is running from. This is a really nifty feature to get you back to the correct instance of Visual Studio if you are running many of them at once.

Clicking on the “Path” will open up Windows Explorer to show you the folder in which the site is located. And clicking “Config” will open the config file in Visual Studio.

Tip of the day #20: Don't spam your own email while developing apps that send email

When we develop applications, often there will be a requirement for that application to send out emails. While this is going on we usually end up with lots of emails being sent to our own email address for test purposes.

I got this fantastic tip from a colleague of mine, Andy Gibson, so here it is:

If you want to test the email an application sends out without spamming your inbox you can modify your web.config with the following code so that it will save the emails to your machine as flat files rather than sending them through the SMTP client. If you combine this with ASP.NET 4 build configurations (web.config.release, web.config.debug, etc,) then this becomes even niftier.

    <smtp deliveryMethod="SpecifiedPickupDirectory">
      <specifiedPickupDirectory pickupDirectoryLocation="D:Email"/>
      <network host="localhost"/> <!-- Required for .NET 4.0! -->

There is an added benefit to this, .NET saves it as a .eml file so your default mail client (in my case Outlook 2007) will open it on double click, or if you need to see the raw email including headers, you can open it in notepad.

Parser Error Message: Ambiguous match found


On deploying a newly precompiled web site, the error message as displayed by ASP.NET is as follows:

Server Error in ‘/’ Application.

Parser Error

Description: An error occurred during the parsing of a resource required to service this request. Please review the following specific parse error details and modify your source file appropriately.

Parser Error Message: Ambiguous match found.

Source Error:

Line 1:  <%@ control language="C#" autoeventwireup="true" enableviewstate="false" inherits="WebSiteControls_Offers_TypeFilter, App_Web_typefilter.ascx.bd9a439f" %>

Line 2:
Line 3: <%--

Source File: /WebSiteControls/Offers/TypeFilter.ascx    Line: 1

Version Information: Microsoft .NET Framework Version:2.0.50727.3053; ASP.NET Version:2.0.50727.3053

System Information

This may or may not be relevant, but it reflects my set up at the time I discovered this issue.

  • Web Site project
  • Visual Studio 2008
  • .NET 3.5
  • IIS 6.0



The reason for this is that a control, in this case a Repeater, in the User Control had a similar name as a field on the code behind. The only difference between the two the case. One was called “offerTypes” (in the code behind, an IEnumerable containing entities from the business layer), the other “OfferTypes” (a ASP.NET Repeater control).


Rename one or other so that the names differ by more that case alone. In my case, I renamed the field referring to the entities to offerTypeInformation.

32bit ASP.NET App on 64bit Windows 7

Recently we all moved to Windows 7 (64bit) on our development machines. With that, we moved also to IIS7.
So that meant that development copies of websites that we are working on have to
be moved to IIS7. This should be simple. Right?

When I attempted to get the first site up and running I got hit by the error “Could not load file or assembly ‘System.Web’ or one of its dependencies. An attempt was made to load a program with an incorrect format.“.
(The full error message is below.)
After searching around I eventually managed to piece together that the reason
was that IIS was trying to run in 64 bit mode and this application was compiled
as a 32 bit application. Okay…. So how do you fix this?

It turns out the fix is rather easy. In the IIS Manager, open up the list of
Application Pools and select the pool for your application. Right-click and
select “Advanced Settings”. From that point you get this dialog:

32-bit application

Just set “Enable 32-bit applications” to “True”, then Okay the dialog. You then
have to recycle the Application Pool for the change to take effect.



Server Error in ‘/’ Application.

Could not load file or assembly ‘System.Web’ or one of its dependencies. An attempt was made to load a program with an incorrect format.

Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.

Exception Details: System.BadImageFormatException: Could not load file or assembly ‘System.Web’ or one of its dependencies. An attempt was made to load a program with an incorrect format.

Source Error:

An unhandled exception was generated during the execution of the current web request. Information regarding the origin and location of the exception can be identified using the exception stack trace below.

Assembly Load Trace: The following information can be helpful to determine why the assembly ‘System.Web’ could not be loaded.

WRN: Assembly binding logging is turned OFF.
To enable assembly bind failure logging, set the registry value [HKLMSoftwareMicrosoftFusion!EnableLog] (DWORD) to 1.
Note: There is some performance penalty associated with assembly bind failure logging.
To turn this feature off, remove the registry value [HKLMSoftwareMicrosoftFusion!EnableLog].

Stack Trace:

[BadImageFormatException: Could not load file or assembly 'System.Web' or one of its dependencies. An attempt was made to load a program with an incorrect format.]
   System.Reflection.Assembly._nLoad(AssemblyName fileName, String codeBase, Evidence assemblySecurity, Assembly locationHint, StackCrawlMark& stackMark, Boolean throwOnFileNotFound, Boolean forIntrospection) +0
   System.Reflection.Assembly.InternalLoad(AssemblyName assemblyRef, Evidence assemblySecurity, StackCrawlMark& stackMark, Boolean forIntrospection) +416
   System.Reflection.Assembly.InternalLoad(String assemblyString, Evidence assemblySecurity, StackCrawlMark& stackMark, Boolean forIntrospection) +166
   System.Reflection.Assembly.Load(String assemblyString) +35
   System.Web.Configuration.CompilationSection.LoadAssemblyHelper(String assemblyName, Boolean starDirective) +190

[ConfigurationErrorsException: Could not load file or assembly 'System.Web' or one of its dependencies. An attempt was made to load a program with an incorrect format.]
   System.Web.Configuration.CompilationSection.LoadAssemblyHelper(String assemblyName, Boolean starDirective) +11207304
   System.Web.Configuration.CompilationSection.LoadAllAssembliesFromAppDomainBinDirectory() +388
   System.Web.Configuration.CompilationSection.LoadAssembly(AssemblyInfo ai) +232
   System.Web.Configuration.AssemblyInfo.get_AssemblyInternal() +48
   System.Web.Compilation.BuildManager.GetReferencedAssemblies(CompilationSection compConfig) +210
   System.Web.Compilation.BuildProvidersCompiler..ctor(VirtualPath configPath, Boolean supportLocalization, String outputAssemblyName) +76
   System.Web.Compilation.CodeDirectoryCompiler.GetCodeDirectoryAssembly(VirtualPath virtualDir, CodeDirectoryType dirType, String assemblyName, StringSet excludedSubdirectories, Boolean isDirectoryAllowed) +11196482
   System.Web.Compilation.BuildManager.CompileCodeDirectory(VirtualPath virtualDir, CodeDirectoryType dirType, String assemblyName, StringSet excludedSubdirectories) +185
   System.Web.Compilation.BuildManager.EnsureTopLevelFilesCompiled() +551

[HttpException (0x80004005): Could not load file or assembly 'System.Web' or one of its dependencies. An attempt was made to load a program with an incorrect format.]
   System.Web.Compilation.BuildManager.ReportTopLevelCompilationException() +76
   System.Web.Compilation.BuildManager.EnsureTopLevelFilesCompiled() +1012
   System.Web.Hosting.HostingEnvironment.Initialize(ApplicationManager appManager, IApplicationHost appHost, IConfigMapPathFactory configMapPathFactory, HostingEnvironmentParameters hostingParameters) +1025

[HttpException (0x80004005): Could not load file or assembly 'System.Web' or one of its dependencies. An attempt was made to load a program with an incorrect format.]
   System.Web.HttpRuntime.FirstRequestInit(HttpContext context) +11301302
   System.Web.HttpRuntime.EnsureFirstRequestInit(HttpContext context) +88
   System.Web.HttpRuntime.ProcessRequestInternal(HttpWorkerRequest wr) +11174792

Version Information: Microsoft .NET Framework Version:2.0.50727.4927; ASP.NET Version:2.0.50727.4927

Tip of the Day #17: Duplicate input fields

Don’t allow duplicate input fields into your form.

The other day I was trying to debug a bug in an application that I maintain. The code created a set of pagination buttons at the top of the page with previous and next buttons. At some point a request had come in that the buttons needed to be replicated at the bottom of the page. Since the HTML was being built up in a string and dumped in a literal control in the first place the developer that was tasked with making the change just dumped the string into two literal controls, the original at the top of the page, and the new one at the bottom of the page. The previous and next buttons use hidden input field to tell the application which actual page number the buttons correspond to. And these were now duplicated and as a result the previous and next buttons ceased to work.

Here is an example of something similar:

<input id="first-hidden-field" value="123" type="hidden" name="some-name" />
<input id="submit-button" value="Submit" type="submit" />
<input id="second-hidden-field" value="456" type="hidden" name="some-name" />

When the form fields are returned to the application and the field “some-name” is queried the result back is a combination of the two fields with the duplicate name. In this case:

string someName = Request.Form["some-name"];

will result in the value of “123,456” being stored in the string. Basically, it is the comma separated form of all the input fields with the given name.

Drawing lines on the map with the Virtual Earth ASP.NET control

This uses the July 2008 CTP of the Windows Live tools. You can download the Windows Live Tools for Visual Studio.

In this post, we’re taking a look at drawing lines on the map with the Virtual Earth ASP.NET control.

In the a previous post (Using PushPins with the Virtual Earth ASP.NET control) I showed how to create a Shape object that represents a point on the map. In that post the Shape constructor took a parameter that described the type of object and a point. With Polylines more than one point is required. To construct an appropriate Shape object a list of points is needed. A point is represented by an LatLongWithAltitude object.

List<LatLongWithAltitude> points = new List<LatLongWithAltitude>();

Once the list is populated the Shape can be created:

Shape result = new Shape(ShapeType.Polyline, points);

Alternatively, it is possible to defer the addition of the points until a later time by assigning them through the Points property.

Lines and Pushpins

One thing you have to be careful of when creating shapes that are lines (or polygons, but we’ll come to that later on) is that by default a pushpin is also displayed to go along with the line (see right) in order to give the user something to hover over so they can gain more information. If these pushpins are not desired then the IconVisible property needs to be set to false.

shape.IconVisible = false;

On the other hand, if a pushpin is desired for each line then you can control where the pushpin appears. By default they are somewhere along the middle of the line. The IconAnchor property can be set to the point at which the pushpin is to appear. For example, to set the pushpin to appear at the start of a line use:

shape.IconAnchor = shape.Points[0];

You can also do the other customising of the pushpin as I showed you in my previous post (Using PushPins with the Virtual Earth ASP.NET control).

Customising the Line

There are also many ways to customise the line as well. You can change the colour and width of the line on the Shape object.

In the example on the left the data has been customised to reflect two attributes. The data is rail services in Scotland and includes information on the route, start and end stations, the length of the route and the train franchise operator.

In the example, the train franchise operator is reflected in the colour of the lines while the length of the route is reflected in the width of the line. The shorter suburban routes are quite thin lines, while the intercity lines are much thicker.

The LineWidth property accepts the width in pixels of the line, while the LineColor property accepts a Color object (from the Microsoft.Live.ServerControls.VE namespace). The Color object allows you to specify the standard red, green and blue values (each 0-255) along with an alpha blend value (a double from 0 to 1.0).

Line Generalisation

So far this is pretty easy, however, what appears on the map isn’t necessarily what you put into the Shape object. Virtual Earth generalises the shape that you create, presumably to improve performance. I suspect the idea was to generalise in a way that the user wouldn’t notice, however, it some situations it is very obvious that the line is not displaying its original points.

Take a look at these two maps. They both display exactly the same data. One is just a zoomed in version of the other (I’ve enlarged the zoomed out version so they are the same size to make them easier to compare).

The map on the left shows the naturally zoomed in version. The one on the right shows the zoomed out version (which I then resized back so that both maps are the same size for this blog post)

Because central Scotland is quite busy and has a lot of rail routes, have a look at the routes in the Highlands (north of Scotland) to see this phenomenon more easily. Take the most northern route for example. The map on the left shows it weaving in and out of the mountains reasonably clearly, while the map on the right shows the route slightly more straightened out. Also the map on the right doesn’t even appear to hit some of the points.


Technorati Tags: ,,

Using a different map server with the Virtual Earth ASP.NET controls.

This uses the July 2008 CTP of the Windows Live tools. You can download the Windows Live Tools for Visual Studio.

One of the interesting features of Virtual Earth is that it doesn’t have to use the maps provided by Microsoft. You can set it up to point to another tile server so that you can use your own maps. This could be extremely useful for people who want to show static visualisations of data, or simply display something different to what Microsoft give you.

Setting up the tile server is likely the more difficult part as setting up the client to point at a new tile server is incredibly easy. I’m not going into setting up the tile server in this post.

First setting up the ASPX page to handle this:

<form id="form1" runat="server">
       <asp:ScriptManager ID="ScriptManager1" runat="server"
         EnablePartialRendering="true" />
       <ve:Map ID="VEMap" runat="server" Height="600px" Width="600px"
         ZoomLevel="8" Center-Latitude="55.75" Center-Longitude="-3.5" />

As you can see there isn’t anything unusual here. There are no additional options to set.

However, in the Page_Load event handler there are some things that need to be set up:

protected void Page_Load(object sender, EventArgs e)
    VEMap.DashboardSize = DashboardSize.Tiny;
    List<LatLongRectangle> bounds = CalculateBounds();
    TileSourceSpecification tileSpec = new TileSourceSpecification(
        "OSM", "", 1, bounds, 1, 18, "getTilePath", 1.0, 100, true);
    VEMap.AddTileLayer(tileSpec, true);
private static List<LatLongRectangle> CalculateBounds()
    List<LatLongRectangle> bounds = new List<LatLongRectangle>();
    LatLongRectangle box = new LatLongRectangle(
        new LatLong(90, -180), new LatLong(-90, 180));
    return bounds;

First of all, we are reducing the dashboard side down to its minimum because the particular tile server does not support the other features provided in the dashboard such as switching between road and aerial photography.

We create the TileSourceSpecification that defines what the tile server can do and how to interact with it. The parameters are:

  • Id: An identifier for you to use to identify the tile server, if you are using more than one.
  • TileSource: You can set up a string format to define the URL of the tiles, but only if your tile server is using the same naming convention as Virtual Earth itself. It has the ability of switching between road, aerial and hybrid modes and load balancing. More info on the TileSource Property…
  • NumServers: If your tile server is load balanced then this specifies the number of servers.
  • Bounds: This is the bounding box of the tile layer. In our case above the tile server covers the entire world so our bounding box covers +90,-180 to -90,+180
  • MinZoomLevel / MaxZoomLevel: This pair of parameters specify the the minimum and maximum zoom levels. 1=the whole world, 18 = street level.
  • GetTilePath: This is the name of a javascript function that generates the path to the tile. I’ll go into more detail on that javascript function a little later on. However, if this property contains a value then the TileSource and NumServers properties are ignored.
  • Opacity: How opaque is this tile layer. By default the Virtual Earth tiles are drawn first, then your layer is drawn on top. 0 is completely transparent (and a bit pointless) while 1.0 is completely opaque and will hide the layer underneath. At present you cannot turn off the layer underneath so if you only want to see your layer set the opacity to 1.0. This is because Virtual Earth ASP.NET controls are based on Virtual Earth 6.1. 6.2 is now released which does have the ability to turn off the base layers.
  • ZIndex: Tile layers can be stacked on top of each other, this indicates the positioning of this layer in that stack.
  • Visible: Indicates whether the layer is visible or not.

In the example above the tile server does not support the virtual earth naming convention. In that case a javascript function to define the name of each tile is needed. In our example it looks like this:

function getTilePath(tileContext)
    return "http://tile.openstreetmap.org/" + tileContext.ZoomLevel +
         "/" + tileContext.XPos + "/" + tileContext.YPos + ".png";

The server for the tiles is http://tile.openstreetmap.org. If you are unfamiliar with it, Open Street Map is an open source map of the world.

The tile server refers to the individual tiles in the format “/{ZoomLevel}/{X}/{Y}.png”. These values can be obtained by the tileContext parameter (see right) passed into the javascript function.

The tile context also contains a map style property which can be “r” (road), “a” (aerial) or “h” (hybrid).

The final result looks like this:

For information about doing the same purely in javascript see this post by John O’Brien.

Errors like these drive me insane

Today I was trying to fix up a website for one of our clients. I got the site out of source control but somehow or other it wouldn’t compile. I’m not going to talk about the fact it didn’t compile out of the box – We all know that is not a good situation and the person who allows source to get into a state like that needs to be slapped repeatedly with a wet fish.

What I’m going to talk about is what the eventual error turned out to be because it is not something I’ve ever seen before and it was such a bizarre thing that I can only hope it isn’t common. But if you are afflicted by it you will be pleased to know that the solution is easy, even if the discovery of what the problem actually was wasted several hours.

If you are reading this then I suspect you will probably be suffering from this problem in which case you are probably now yelling “STOP BABBLING MAN AND TELL ME WHAT TO DO”.

First, a description of the problem:

There is an ASP.NET web site project (probably does the same thing on a web application… And let’s not get in to why this is a web site project, it’s an old project and the standard now is web applications) with a number of web forms in it.

The ASPX for the default page currently looks something like this:

<%@ Page Language="C#" AutoEventWireup="true"
      CodeFile="Default.aspx.cs" Inherits="Default" %>
    <form id="form1" runat="server">
        Stuff that's on my page
        <asp:Label ID="MyLabel" runat="server"></asp:Label>

Nothing too odd about that, you might say… And you’d be right. There is nothing at all wrong with this page. However, when you go to compile your website you get this error:

Infuriation 2
The name ‘MyLabel’ does not exist in the current context

But… but… but… You can see that MyLabel exists on the ASPX page and if you type in the C# or VB source file you’ll see that intellisense finds the object perfectly well. So what is going on?

Well, it is interesting to note that Page1 and Page2 are very similar to Default. In fact, so similar that when they were created the person that did this just copied Default.aspx and Default.aspx.cs (or Default.aspx.vb if that’s your poison). What they didn’t do when they made the copies was to change the page directive at the top that has the Inherits attribute that points to Default. So, Page1 and Page2 are inheriting the behaviour in Default.

When this first happened that wasn’t a problem. Page1 and Page2 had just minor cosmetic differences, the behaviour was the same and no one noticed.

At some point later someone came along and added MyLabel to Default.aspx… This still didn’t make a difference. Everything worked as normal.

Then someone came along and realised that MyLabel needed to change on some condition and added some code into the Default.aspx.cs file that modified MyLabel. At this point all hell broke loose!

Suddenly, MyLabel can’t be found and no one can figure out why. It is there on the ASPX page, intellisense picks it up, the stupid compiler can’t see it.

The Solution

Eventually, after spending a couple of hours on the problem and batting it around some collegues and doing bit of brainstorming someone (let’s call him Craig Muirhead because he figured it out in the end and deserves the credit) comes up with the idea that perhaps other pages are inheriting the wrong class. A quick find in files on the name of the class and we found it was referenced by 4 other pages. It takes a matter of moments to fix all those files to point to their respective code behind files/classes rather than the one on our hapless page. And all of a sudden everything compiles.

Technorati Tags: ,,,,

Using PushPins with the Virtual Earth ASP.NET control

This uses the July 2008 CTP of the Windows Live tools. You can download the Windows Live Tools for Visual Studio.

In this post, we’re taking a look at using pushpins with the Virtual Earth ASP.NET control. We have a page similar to the previous post, with a map control on it called VEMap.

In the code behind we have a method for adding shapes to the map as this is a multi-step process. First we need to create an object to represent the point, then an object to represent the shape (we’ll come to other shape types later, but for the moment, we’re just dealing with pushpins), finally we add the shape to the map.

The Page_Load method adds a number of shapes to the map, these pushpins represent locations where Scottish Developers have held user group meetings. The code looks like this:

protected void Page_Load(object sender, EventArgs e)
    // Glasgow Caledonian University
    AddShape(55.8662120997906, -4.25060659646988);

    // Dundee University
    AddShape(56.4572643488646, -2.97848381102085);

    // Microsoft Edinburgh (George Street)
    AddShape(55.9525336325169, -3.20506207644939);

    // Microsoft Edinburgh (Waterloo Place)
    AddShape(55.9535374492407, -3.18680360913277);

private void AddShape(double latitude, double longitude)
    LatLongWithAltitude point = new LatLongWithAltitude(latitude, longitude);
    Shape shape = new Shape(ShapeType.Pushpin, point);

From this we get a fairly standard output when the application is run:

At present, this is all visual. There isn’t any real functionality. What we’ll do is add some very basic functionality, so that when you hover over a pushpin it tells you something about it. The Shape object has a Description property into which you can put an HTML fragment. So, here is the updated code:

protected void Page_Load(object sender, EventArgs e)
    // Glasgow Caledonian University
    AddShape(55.8662120997906, -4.25060659646988,
        "<b>Glasgow Caledonian University</b>");

    // Dundee University
    AddShape(56.4572643488646, -2.97848381102085,
        "<b>Dundee University</b>");

    // Microsoft Edinburgh (George Street)
    AddShape(55.9525336325169, -3.20506207644939,
        "<b>Microsoft Edinburgh</b> (George Street)");

    // Microsoft Edinburgh (Waterloo Place)
    AddShape(55.9535374492407, -3.18680360913277,
        "<b>Microsoft Edinburgh</b> (Waterloo Place)");

private void AddShape(double latitude, double longitude, string description)
    LatLongWithAltitude point = new LatLongWithAltitude(latitude, longitude);
    Shape shape = new Shape(ShapeType.Pushpin, point);
    shape.Description = description;

The result when you hover over a pushpin looks like this:

That’s all great if you want the default pushpin look. However, you might want to customise the pins so they match more what you are looking for. The Shape class has a CustomIcon property which you can set to be a graphics object. In the following example I’ve used a simple png file with a red circle and an semi-transparent yellow fill.

The code now looks like this:

private void AddShape(double latitude, double longitude, string description)
    LatLongWithAltitude point = new LatLongWithAltitude(latitude, longitude);
    Shape shape = new Shape(ShapeType.Pushpin, point);
    shape.Description = description;
    shape.CustomIcon = "images/target.png";

And the result looks like this:

Finding things with Virtual Earth

This uses the July 2008 CTP of the Windows Live tools. You can download the Windows Live Tools for Visual Studio.

This is a very introductory post just to show how to find things using the Virtual Earth ASP.NET control.

First you need to add an assembly reference to the Virtual Earth control to your project:

In each page that you want to use the Virtual Earth control you must add a line that looks like this at the top of the file:

<%@ Register Assembly="Microsoft.Live.ServerControls.VE"
    TagPrefix="ve" %>

This registers the assembly allowing you to use the control.

When you view the ASPX page you will see that you have additional tools in the toolbox that relate to Virtual Earth. The one we are going to look at in this post is the Map control.

From the tool box you can drag a map control onto your design surface. The code it generates will set up a default position and zoom level which centres on the continental United States.

By default the control is a 400x400px square and has been given a name of Map1:

<ve:Map ID="Map1" runat="server" Height="400px" Width="400px" ZoomLevel="4" />


To start with we are going to change the defaults to something that is closer to home (well, mine at least) and centre it on central and southern Scotland and zoom in somewhat. I also don’t like the name Map1 so I’m going to change that too:

<ve:Map ID="VEMap" runat="server" Height="600px" Width="400px" ZoomLevel="8"  Center-Latitude="55.75" Center-Longitude="-3.5" />

The first thing I should comment on is the zoom level because it doesn’t really mean anything to anyone. Personally, I’d like to say “here’s a bounding box for the area I want to see, you figure out how to do that and sort out the aspect ratio for me”. Then again, maybe that’s because when I wrote a GIS system for my final year project at university that was what I did. I didn’t constrain the user to specific and artificial zoom levels. The maths behind it isn’t difficult and a modern graphics card can do that with its proverbial eyes closed. Having said that I can understand why it was done that way. It means that none of the maps are generated on the fly, it is all based on pre-existing graphic files that are retrieved as needed. This means no strained servers trying to render maps.

The zoom level ranges from 1 to 19. 1 is zoomed out to the whole world and 19 is zoomed in to street level. In between that it seems to be mostly an matter of experimentation.

As it stands the program will display a map on the page and you can zoom in or out, pan around and change display modes and so on, just like Live Maps.

Next, we’ll add some functionality to find stuff. To that end a text box (SearchTextBox) will be added in order that we can type stuff in, and a button (SearchButton) that we can submit it. The code for the button click event is as follows:

protected void SearchButton_Click(object sender, EventArgs e)
    VEMap.Find(string.Empty, SearchTextBox.Text);

The two parameters on the Find method match the two text boxes you find on Live Maps. The first parameter is the “what” (i.e. the business name or category” and the second parameter is the “where” (i.e. The address, location or landmark). If you use Live Maps a lot you’ll probably already by used to just ignoring the first box, so I haven’t included anything to populate that parameter and will just leave it empty.

Now, when the application is run the map will update when the button is clicked. It will zoom to the location you’ve specified.

At present there is no mechanism to determine where to move the map to if there is any ambiguity. For example, type “Bolton” and you’ll be taken to Bolton, England rather than Bolton, NY. Type “Washington” and you’ll be taken to the District of Columbia rather than the state. On the other hand type WA (the standard two letter abbreviation for Washington State) and you will be taken to Washington state.

The Virtual Earth control can tell you about all the places that it thought about when it was deciding where to take you. To get that we have to handle the ServerFind event on the map control. In order to do that change the ASPX markup to read:

<ve:Map ID="VEMap" runat="server" OnServerFind="VEMap_ServerFind"  Height="600px" Width="400px" ZoomLevel="8"  Center-Latitude="55.75" Center-Longitude="-3.5" />

And then add a handler in the code behind:

protected void VEMap_ServerFind(object sender, FindEventArgs e)
    StringBuilder sb = new StringBuilder();

    foreach (Place place in e.Places)
        sb.Append("</strong> (");
        sb.AppendFormat("Lat: {0}, Long: {1}", place.LatLong.Latitude, place.LatLong.Longitude);
    ResultLiteral.Text = sb.ToString();

Note: A Literal control called ResultLiteral has also been added to the page to display the results.

The ServerFind event will be raised by the map control when it finds stuff, however, you’ll notice that the page does not include the text we’ve built up. You might be thinking at this point that the event isn’t being raised at all, but put a break point down inside the code of the event. You’ll see the breakpoint is being hit.

The problem is that the ServerEvent is being handled as part of an AJAX postback rather than a page postback. If you look at the stack trace you’ll see that the map control has its own internal UpdatePanel that you would normally need to indicate that part of the page was AJAXified, so to speak. So to ensure that the code works as we would expect it to we need to add some things to the ASPX file.

First off we need a ScriptManager:

<asp:ScriptManager ID="ScriptManager1" runat="server" EnablePartialRendering="true">

And secondly we need an update panel of our own in order to put the controls that will be updated when the ServerFind event is handled. So the update panel, with the controls we created earlier, looks something like this.

<asp:UpdatePanel ID="UpdatePanel1" runat="server">
        <asp:TextBox ID="SearchTextBox" runat="server" />
        <asp:Button ID="SearchButton" runat="server" Text="Search"
          onclick="SearchButton_Click" />
        <asp:Literal ID="ResultLiteral" runat="server" />

If we search for Bolton again the results look like this:

As you can see there are several Boltons in the world.

The FindEventArgs contains many bits of information, but in the sample above we’ve just concentrated on the Place. This gives you details of the places that have been found, how good a match it thinks the place is and where the place actually is. Obviously, the more specific you are in the search the more accurate the results are going to be and the more chance you have of getting an exact match.

Note: At present there is not much documentation for the Virtual Earth ASP.NET control. Much of the functionality has been gleaned from reading the documentation for the “classic” Virtual Earth control which is customisable through Javascript. I also found a bug, which has been reported to Microsoft, that if you happen to be in Birds Eye view then the find functionality does not work.