Sunday, August 2, 2009

Part IV: Step by step procedure of how to install an assembly

Posted on/at 7:34 AM by Admin


Introduction

Now, we will see how to install an assembly using the merge module witch is a tool provided by the MS Visual Studio 2005. The merge module tool is used to wrap components designed especially in order to be shared later. Components can be dll files or user controls objects that are consumed, in general, by the developer who wants to use or reuse already existing components, for example, he can develop anapplication against given dll files. So, using this method can provide him possibility to enjoy with those dll's or component's services.
Assuming that we want deploy an assembly witch called ClassLibrary1 with this feature:

using System;

using System.Collections.Generic;

using System.Text;

using System.Windows.Forms;

namespace ClassLibrary1

{

public class Class1

{

public Class1()
        {

             MessageBox.Show("You are using ClassLibrary1");

        }

}
}
After building it, we save the project and we add a new one by clicking File, New and Project 
clip_image001
Figure 1

After the merge module project is opened, select Module Retarget able Folder just at right under the File system target machine node, then, right click and select add, then select project output as the figure shows bellow:
clip_image002
Figure 2

Select the primary output element list representing the assembly that we want to deploy. If you want to deploy other elements such as documentation files to get support to the developers or XML serialization assemblies, you can add them with the primary output.
clip_image003
Figure 3

Now, expand the Merge module properties grid and set the author name, for example, "Me" as the figure shows bellow:
clip_image004
Figure 4
There is a property witch I find very useful by the way, I mean, the "Search Path" property used to determine the path used to localize assemblies, files or even merge modules on the development computer.
Now, build the solution and browse to the application directory, the merge module project with name "MergeModule1" is there. Browse to the debug file an open it. A file with *.msm extension is created; this file represents the merge module project output. The mission is not completely accomplished because the merge module can not be installed directly. To install it, we must add an Installer project in order to consume the merge module; in fact, this one can't be installed by its self.
So, add a new setup project by selecting File then Add then New Project, and select Setup Project
clip_image005
Figure 5

Now, select Application Folder then Add then Project output menu item and click on it.
clip_image006
Figure 6

Select the Merge Module 1 project in the combo box list as shown bellow:
clip_image007
Figure 7

Expand the setup properties grid and change the author property "." By "Me" and the Manufacturer property value "." By "Me" before build the project, otherwise, it can not be installed later. Build the setup project. After that, select it, right click and choose install in the context menu and click on it.
clip_image008
Figure 8
The install process will be launched.
clip_image009
Figure 9

After the project deployment, swap to the configuration panel and open add and remove program. You can find the setup 1 among the installed programs.
clip_image010
Figure 10
The developer, or let us say the intermediate user, can browse to %root%\ProgramFiles\Me, there, he can find the new installed dll file.

Part III: Step by step procedure of how to install an assembly

Posted on/at 7:31 AM by Admin


Introduction

We have seen in previous articles how to install an assembly using both, the .NET Framework management console and the Global assembly cache tool provided by the NET Framework. Now we will se how to install a strong named assembly with the simplest manner.
We have seen in a previous article  how one can install a given assembly using the .Net Framework management console the Mscorcfg.msc. Now, we proceed to achieve the same goal but using another tool witch is the Global assembly cache tool cagutil.exe provided by the .Net framework.
To do that, follow those steps:
First of all, let us develop a simple assembly. To do that, follow those steps

  • Create a new class library project and name it myAssembly

clip_image001
Figure 1

  • Add this code to the editor

using System;

using System.Collections.Generic;

using System.Text;

using System.Windows.Forms;

namespace myAssembly

{

public class Class1

{

public Class1()

{

MessageBox.Show("You are using myAssembly");

}

}
}

  • Save the project.
    The GAC accepts only assemblies with strong names; there fore it is imperatively recommended to sign the assembly before adding it into GAC, otherwise when adding a none strongly assembly, a message indicates that the new assembly can not be added to the GAC appears and finally the action is failed. So, to sign myAssembly, go to myAssembly properties as shown bellow.

clip_image002
Figure 2

  • Select  the signing tab and then check sign the assembly checkbox

clip_image003
Figure 3

  • Select New in the combo box as shown above. This dialog box appears

clip_image004
Figure 4

  • Enter the key file name with twelve characters, then enter a password with more than six characters and confirm it, then click Ok.
    A file looks like this clip_image005 is added to the application directory with *.snk as an extension witch is a Strong Name Key abbreviation. This file contains a random pair keys and it is provided to sign the assembly. This file can be generated also using the Strong name tool sn.exe provided by .NET framework.
  • Browse to "C:\Windows\assembly" directory witch looks like bellow:

clip_image006
Figure 5

  • Drag the assembly and drop it in this directory, that's all.

Installing an Assembly: Part II Using the Global Cache

Posted on/at 7:28 AM by Admin

Introduction

We have seen in a previous article  how one can install a given assembly using the .Net Framework management console the Mscorcfg.msc. Now, we proceed to achieve the same goal but using another tool witch is the Global assembly cache tool cagutil.exe provided by the .Net framework.
To do that, follow those steps:
First of all, let us develop a simple assembly. To do that, follow those steps

  • 1. Create a new class library project and name it myAssembly

@1.gif
Figure 1

  • Add this code to the editor

using System;

using System.Collections.Generic;

using System.Text;

using System.Windows.Forms;

namespace myAssembly

{

public class Class1

{

public Class1()

{

MessageBox.Show("You are using myAssembly!!!");

}

}
}

  • Save the project.
    The GAC accepts only assemblies with strong names ; there fore it is imperatively recommended to sign the assembly before adding it into GAC, otherwise when adding a none strongly assembly, a message indicates that the new assembly can not be added to the GAC appears and finally the action is failed. So, to sign myAssembly, go to myAssembly properties as shown bellow.

@2.gif
Figure 2

  • Select the signing tab and then check sign the assembly checkbox

@3.gif
Figure 3

  • Select New in the combo box as shown above. This dialog box appears

@4.gif
Figure 4

  • Enter the key file name with twelve characters, then enter a password with more than six characters and confirm it, then click Ok.
    A file looks like this small.gif is added to the application directory with *.snk as an extension witch is a Strong Name Key abbreviation. This file contains a random pair keys and it is provided to sign the assembly. This file can be generated also using the Strong name tool sn.exe  provided by .NET framework.

  • Build the solution, save the project and close it.

  • Open the console, Start--> Accessories--> Console

  • Browse to your .Net framework directory using the change directory cd command.

  • Tape cagutil -I  myAssembly and the press enter and the windows shell will tell you that the assembly is installed in the assembly cache.

Step by step procedure of how to install an assembly: Part I

Posted on/at 7:27 AM by Admin

Introduction

It is an assembly container provided by the .NET Framework especially to store assemblies shared and used by different .Net applications installed on a given computer or on a given network. It is located in a subdirectory of the root system but it can not be directly accessed except that administrator permission is given before. The GAC is accessed via the .NET configuration management console witch is represented bellow:
1.gif
Figure 1
Assume that an assembly is simultaneously used and/or reused by more than one developer or shared by more than one application. So, it should be shared by them all. Therefore using GAC is a good alternative. It is possible to install the assembly in the GAC and give developers privileges access to the system root rather than to copy it into each development station. It is possible that the assembly cache contains more than one version of the same assembly but it is important to mention in this context that assemblies' names don't have an extension like *.dll or *.exe when are displayed within the GAC list. Only the assembly name appears without any extensions, because when the common language runtime CLR looks for a targeted assembly, it can precise the corresponding assembly according to the application needs. 
When an application is developed against an assembly, it depends on it, therefore it needs to locate it and exploit its services when running. First of all the common language runtime CLR, witch is responsible for executing the application program, verifies if the assembly is already referenced and used, if this is not the case, it search it, at second plan, in the application bin directory, after that, it is checked in the global assembly cache GAC, in a final step, the CLR checks information about the targeted assembly in the configuration file, in this step the code base located in the configuration file gives information about the targeted assembly. When the assembly is not or can not be found, an error occurs. And a message like this can be found in the error list:
2.gif
Figure 2

Where can one find the Global Assembly Cache:

To obtain the assemblies' list in Global assembly cache, follow this path:
Click Start --> Configuration Panel --> Administration tools --> .Net Framework Configuration 
The .NET Framework 2.0 Configuration management console is opened as bellow:
3.gif
Figure 3

Expand the My Computer node within the tree just at right and then select the assembly cache node then click view List of assemblies in the assembly cache hyper link.
4.gif
Figure 4

The assembly list is displayed as bellow:
5.gif
Figure 5

How can one install an assembly in the GAC:

First of all, let us develop a simple assembly. To do that, follow those steps

  • Create a new class library project and name it myAssembly for example.

6.gif6.gif
Figure 6

  • Add this code to the editor.

using System;

using System.Collections.Generic;

using System.Text;

using System.Windows.Forms;

namespace myAssembly

{

public class Class1

{

public Class1()

{

MessageBox.Show("You are using myAssembly");

}

}

}

  • Save the project.
    The GAC accepts only assemblies with strong names ; there fore it is imperatively recommended to sign the assembly before adding it into GAC, otherwise when adding a none strongly assembly, a message indicates that the new assembly can not be added to the GAC appears and finally the action is failed. So, to sign myAssembly, go to myAssembly properties as shown bellow.

7.gif
Figure 7

  • Select the signing tab and then check Sign the assembly checkbox.

8.gif
Figure 8

  • Select New in the combo box as shown above. This dialog box appears

9.gif
Figure 9

  • Enter the key file name with twelve characters, then enter a password with more than six characters and confirm it, then click Ok.
    A file looks like this small.gif is added to the application directory with *.snk as an extension witch is a Strong Name Key abbreviation. This file contains a random pair keys and it is provided to sign the assembly. This file can be generated also using the Strong name tool sn.exe provided by .NET framework.

  • Build the solution, save the project and close it.

  • Switch to the .Net Framework management console and right click on the Assembly cache node, a context menu appears.

  • Click add menu item, the open dialog box appears.

10.gif
Figure 10

  • Browse to the assembly location and then click open. The assembly will be added to the global assembly cache without any problems.

Google's Static Map API WebControl

Posted on/at 12:32 AM by Admin

· Download source - 5.56 KB

· Download demo - 19.24 KB

Introduction

GStaticMap is an ASP.NET WebControl allowing you to build and show a static map image with optionally some markers, a path, etc. using Google’s Static Maps API.

Here is a quick sample, showing you what you can quickly and easily produce with it:

clip_image001

This sample can be achieved with some lines of code in the code-behind, or directly in your ASPX page:

clip_image002 Collapse

<asp:GStaticMap ID="gsmTestMap" AutomaticZoomLevel="true"

AutomaticCenter="true" Width="320" Height="200" Type="RoadMap"

ZoomLevel="12" Key="put_yours_here" runat="server">

<Center Latitude="45.759723" Longitude="4.842223"></Center>

<Markers>

<asp:GMarker Latitude="45.859723" Longitude="4.842223" Letter="a"

Color="Red"></asp:GMarker>

<asp:GMarker Latitude="45.759723" Longitude="5.042223" Letter="b"

Color="Green"></asp:GMarker>

<asp:GMarker Latitude="45.659723" Longitude="4.842223" Letter="c"

Color="Blue"></asp:GMarker>

<asp:GMarker Latitude="45.759723" Longitude="4.642223" Color="Green">

</asp:GMarker>

</Markers>

<Paths>

<asp:GPath Color="blue" Opacity="255" Weight="5">

<Points>

<asp:GCoordinate Latitude="45.859723" Longitude="4.842223">

</asp:GCoordinate>

<asp:GCoordinate Latitude="45.759723" Longitude="5.042223">

</asp:GCoordinate>

<asp:GCoordinate Latitude="45.659723" Longitude="4.842223">

</asp:GCoordinate>

</Points>

</asp:GPath>

<asp:GPath Color="red" Opacity="128" Weight="5">

<Points>

<asp:GCoordinate Latitude="45.659723" Longitude="4.842223">

</asp:GCoordinate>

<asp:GCoordinate Latitude="45.759723" Longitude="4.642223">

</asp:GCoordinate>

<asp:GCoordinate Latitude="45.859723" Longitude="4.842223">

</asp:GCoordinate>

</Points>

</asp:GPath>

</Paths>

</asp:GStaticMap>

The generated URL is the following (I've added some spaces to wrap the URL and removed my API key):

clip_image002[1] Collapse

http://maps.google.com/staticmap?size = 320x200 & maptype = roadmap

& markers = 45.859723,4.842223,reda | 45.759723,5.042223,greenb |

45.659723,4.842223,bluec | 45.759723,4.642223,green & path = rgba:0x0000ffff,weight:5

| 45.859723,4.842223 | 45.759723,5.042223 | 45.659723,4.842223 &

path = rgba:0xff000080,weight:5 | 45.659723,4.842223 | 45.759723,4.642223

| 45.859723,4.842223 & key = put_yours_here.

Note that this WebControl is a sequel of my previous MapData WebControl, replacing it.

Background

First, you'll need to sign up for a free Google Maps API key if you don't have one. Connect with your Google account and put your domain name root (including the TCP/IP port).

Please note that I didn't provide my API key in the sample project, you'll have to put yours if you want to run the tests (just replace all "put_yours_here" occurrences with your key)

Classes Overview / Code Design

Our goal is to display a map with, optionally, some markers and a path. Since the map will be rendered as an image (img tag), there is no need to reinvent the wheel: we'll derive our GStaticMap WebControl from System.Web.UI.Image.

It’s now time to think about the classes that will be the foundations of our WebControl. First, we have a static map (GStaticMap class); this map will use:

  • A center based on a coordinate (GCoordinate class)
  • A collection of markers (GMarker class) themselves based on coordinates (GCoordinate class)
  • A collection of paths (GPath class), themselves based on a collection of coordinates (GCoordinate class)

Here is the class diagram (included in the sample project):

clip_image004

The next step is to more deeply describe our classes. So:

A static map is defined by the following properties:

  • A width (integer, inherited from Image)
  • A height (integer, inherited from Image)
  • A center (using our GCoordinate class, optional when there is at least one marker)
  • A zoom level (integer, optional when there is at least one marker, from 1 to 19)
  • A rendering type (enumerator, optional: roadmap by default, or designed for mobile devices)
  • An image format (enumerator, optional: GIF, JPEG or PNG)

A marker is defined by the following properties:

  • A color (enumerator, optional: the Static Map API restricts them to red, by default, blue, or green)
  • An optional letter (character, optional, from A to Z)

A path is defined by the following properties:

  • A color (you can define it either with a known color from RGB values)
  • An optional opacity (integer, optional: from 0 to 255)

A coordinate is defined by the following properties:

  • A latitude (double)
  • A longitude (double)

Making the Code – Coordinate Class (GCoordinate)

It’s now time for some code.

We'll start our control with the coordinate class (GCoordinate). It’s a "small" class based on two doubles, constructors, and a query string serialization. Here is the class (tips and tricks are explained after):

clip_image002[2] Collapse

/// <summary>

/// Represents a Google Coordinate

/// </summary>

[Serializable]

[ToolboxData("<{0}:GCoordinate Latitude=''

Longitude='' runat="server"></{0}:GCoordinate>")]

public class GCoordinate

{

#region Instance members

/// <summary>

/// Latitude

/// </summary>

private double _latitude;

/// <summary>

/// Longitude

/// </summary>

private double _longitude;

#endregion

#region Accessors

/// <summary>

/// Latitude

/// </summary>

[Bindable(true)]

[Category("Coordinate")]

[DefaultValue("")]

[Localizable(false)]

public double Latitude

{

get { return _latitude; }

set { _latitude = value; }

}

/// <summary>

/// Longitude

/// </summary>

[Bindable(true)]

[Category("Coordinate")]

[DefaultValue("")]

[Localizable(false)]

public double Longitude

{

get { return _longitude; }

set { _longitude = value; }

}

#endregion

#region Constructors

/// <summary>

/// Constructor

/// </summary>

/// <param name="latitude"></param>

/// <param name="longitude"></param>

public GCoordinate()

{

}

/// <summary>

/// Constructor

/// </summary>

public GCoordinate(double latitude, double longitude)

{

SetCoordinate(latitude, longitude);

}

#endregion

#region Utilities

/// <summary>

/// Serialize data as a query string part

/// Format : {latitude,longitude}

/// </summary>

/// <returns></returns>

internal virtual string SerializeAsQueryString()

{

return string.Format("{0},{1}", AngularValueToString(Latitude, 6),

AngularValueToString(Longitude, 6));

}

/// <summary>

/// Format an angular value to be used by GStaticMap

/// </summary>

/// <param name="angularValue"></param>

/// <param name="fixedLength"></param>

/// <returns></returns>

private string AngularValueToString(double angularValue, int precision)

{

return angularValue.ToString("F" + precision,

CultureInfo.CreateSpecificCulture("en-US"));

}

internal void SetCoordinate(double latitude, double longitude)

{

_latitude = latitude;

_longitude = longitude;

}

#endregion

}

First, I've added the [Serializable] attribute before the class declaration, this is needed in order to store the class in the ViewState (as a serializedstring).

Secondly, let's have a look at the serialization methods: the AngularValueToString method converts a double value to a formatted string (localized to en-US culture for the decimal point), and SerializeAsQueryString is an internal (to be accessed by other classes of the same assembly) method making use of AngularValueToString to output the coordinate as query string parameters (braces are not relevant).

Output: {latitude},{longitude}

Finally, we have a SetCoordinate method that will allow us to modify the latitude and longitude in a single call.

Making the Code – Marker Class (GMarker)

A marker will be outputted like this on the map: clip_image006 (color and letter are customizable)

First, we have an enumerator with available colors:

clip_image002[3] Collapse

#region Enumerators

/// <summary>

/// Available Google Static Marker colors

/// </summary>

public enum GMarkerColor

{

Red,

Blue,

Green

}

#endregion

Then, the marker class (GMarker) inherits from GCoordinate and extends it with marker’s color and letter (tips and tricks are explained after):

clip_image002[4] Collapse

/// <summary>

/// Represent a Google Marker

/// {latitude} (required) specifies a latitudinal value with precision to

/// 6 decimal places.

/// {longitude} (required) specifies a longitudinal value with precision to

/// 6 decimal places.

/// {color} (optional) specifies a color from the set {red,blue,green}.

/// {alpha-character} (optional) specifies a single lowercase alphabetic character

/// from the set {a-z}.

/// </summary>

[Serializable]

[ToolboxData("<{0}:GMarker Latitude='' Longitude=''

Letter='a' Color='Red' runat="server">

</{0}:GMarker>")]

public class GMarker : GCoordinate

{

#region Instance members

/// <summary>

/// Marker color

/// </summary>

private MarkerColor _color;

/// <summary>

/// Marker letter (optional)

/// </summary>

private char? _letter;

#endregion

#region Accessors

/// <summary>

/// Marker color

/// </summary>

[Bindable(true)]

[Category("Appearance")]

[DefaultValue("Red")]

[Localizable(false)]

public MarkerColor Color

{

get { return _color; }

set { _color = value; }

}

/// <summary>

/// Marker letter (optional)

/// </summary>

[Bindable(true)]

[Category("Appearance")]

[DefaultValue("a")]

[Localizable(false)]

public char? Letter

{

get { return _letter; }

set { _letter = value; }

}

#endregion

#region Constructors

public GMarker()

{

}

public GMarker(double latitude, double longitude, MarkerColor color)

: base(latitude, longitude)

{

_color = color;

}

public GMarker(double latitude, double longitude, MarkerColor color, char letter)

: this(latitude, longitude, color)

{

_letter = letter;

}

#endregion

#region Utilities

/// <summary>

/// Serialize data as a query string part

/// Format : {latitude},{longitude},{color}{alpha-character}

/// </summary>

/// <returns></returns>

internal override string SerializeAsQueryString()

{

return string.Format

(

"{0},{1}{2}",

base.SerializeAsQueryString(),

Enum.GetName(typeof(MarkerColor), Color).ToLower(),

(Letter == null ? "" : Letter.ToString())

);

}

#endregion

}

Once again, I added the [Serializable] attribute (always for the ViewState).

The most interesting code is the serialization part: SerializeAsQueryString is again with internal visibility (to be accessed by other classes of the same assembly) and outputs the marker as query string parameters with the help of its base class (for the coordinate part). It also shows how to retrieve the name of an enumerator value (yes, it’s verbose…) transformed to lower characters in order to respect the Google Static Map API (braces are not relevant).

Serialization output: {coordinate},{color}{letter}

Note that the letter is optional, so we only output it if it’s not null (thanks to the ternary operator, gracefully used here).

Making the Code – Path Class (GPath)

The last step before building the static map class is the path class (GPath).

First, we have an enumerator with available color types (simple RBG, or RGB with Alpha channel):

clip_image002[5] Collapse

#region Enumerators

/// <summary>

/// Available Google Static Marker colors

/// </summary>

public enum GPathColorType

{

RGB,

RGBA

}

#endregion

This class doesn't inherit from any class but makes use of the coordinate one (GCoordinate) to store the points (tips and tricks are explained after):

clip_image002[6] Collapse

/// <summary>

/// Represent a Google Path

/// </summary>

[Serializable]

[ToolboxData("<{0}:GPath runat="server"></{0}:GPath>")]

public class GPath

{

#region Enumerators

/// <summary>

/// Available Google Static Marker colors

/// </summary>

protected enum PathColorType

{

RGB,

RGBA

}

#endregion

#region Instance members

/// <summary>

/// Path color (default blue)

/// </summary>

private System.Drawing.Color _color = System.Drawing.Color.Blue;

/// <summary>

/// Path thickness, in pixels

/// </summary>

private int _weight = 1;

/// <summary>

/// Path opacity from 0% to 100% (optional)

/// </summary>

private int? _opacity;

/// <summary>

/// List of points

/// </summary>

private List<GCoordinate> _points = new List<GCoordinate>();

#endregion

#region Accessors

/// <summary>

/// Path color

/// </summary>

[Bindable(true)]

[Category("Appearance")]

[DefaultValue("Red")]

[Localizable(false)]

public System.Drawing.Color Color

{

get { return _color; }

set { _color = value; }

}

/// <summary>

/// Path thickness, in pixels

/// </summary>

[Bindable(true)]

[Category("Appearance")]

[DefaultValue("1")]

[Localizable(false)]

public int Weight

{

get { return _weight; }

set { _weight = value; }

}

/// <summary>

/// Path opacity from 0 to 255 (optional)

/// </summary>

[Bindable(true)]

[Category("Appearance")]

[DefaultValue("1")]

[Localizable(false)]

public int? Opacity

{

get { return _opacity; }

set { _opacity = value; }

}

/// <summary>

/// Paths (optional)

/// </summary>

[

DesignerSerializationVisibility(DesignerSerializationVisibility.Content),

PersistenceMode(PersistenceMode.InnerDefaultProperty),

Editor(typeof(List<GCoordinate>), typeof(System.Drawing.Design.UITypeEditor)),

]

public List<GCoordinate> Points

{

get { return _points; }

}

#endregion

#region Constructors

public GPath()

{

}

public GPath(System.Drawing.Color color, int weight)

{

Color = color;

Weight = weight;

}

public GPath(System.Drawing.Color color, int weight, int opacity)

: this(color, weight)

{

Opacity = opacity;

}

#endregion

#region Utilities

/// <summary>

/// Serialize data as a query string part

/// Format : path=pathColorType:pathColorValue,weight:pathWeight

/// |pathPoint1|pathPoint2|pathPoint3|...

/// </summary>

/// <returns></returns>

internal string SerializeAsQueryString()

{

// Checks

// Get color type

PathColorType pathColorType = (Opacity == null) ? PathColorType.RGB :

PathColorType.RGBA;

// Get color value

string colorValue = string.Format("{0:x8}", Color.ToArgb()).Substring(2, 6);

if (Opacity != null)

{

colorValue += string.Format("{0:x2}", Opacity);

}

// Serialize path's header

string serializedPath = string.Format

(

"path={0}:0x{1},weight:{2}|",

(pathColorType == PathColorType.RGB) ? "rgb" : "rgba",

colorValue,

_weight

);

//Serialize path's points

List<string> serializedPoints = new List<string>();

foreach(GCoordinate coordinate in Points)

{

serializedPoints.Add(coordinate.SerializeAsQueryString());

}

// Returns serialized path

return serializedPath + string.Join("|", serializedPoints.ToArray());

}

#endregion

}

As usual, this class is serializable ([Serializable] attribute) in order to be stored in the ViewState.

The serialization part (SerializeAsQueryString method) is again with internal visibility (to be accessed by other classes of the same assembly) and outputs the path as query string parameters with the help of the coordinate class (braces and brackets are not relevant).

Serialization output: path={rgb|rgba}:0x{ color}[{alpha}],weight:{weight}|{ coordinate}|{coordinate}...

Note that the opacity is optional and the color type will be output as "rgb" or "rgba" according to its value (note the use of the ternary operator).

Making the Code – Static Map Class (GStaticMap)

Here is the final part of our WebControl: the WebControl itself. I've sliced out the interesting parts in order to comment them step by step (you may open the whole code in your Visual Studio).

First, we have useful enumerators for the map type, the map’s image format and for the zoom levels bounds:

clip_image002[7] Collapse

#region Enumerators

/// <summary>

/// Available map types

/// </summary>

public enum GStaticMapType

{

Mobile,

RoadMap

}

/// <summary>

/// Available image formats

/// </summary>

public enum GStaticMapImageFormat

{

GIF,

JPG,

PNG32

}

/// <summary>

/// Available zoom levels

/// </summary>

public enum GStaticMapZoomLevel : uint

{

Mini = 1,

Maxi = 19

}

#endregion

Here is the declaration of the static map class (GStaticMap), inheriting from System.Web.UI.WebControls.Image:

clip_image002[8] Collapse

[ToolboxData("<{0}:GStaticMap runat="server"></{0}:GStaticMap>")]

public class GStaticMap : System.Web.UI.WebControls.Image

{

..........

}

We declare private instance members of our class.

You'll notice that we have members relating to the automatic centering and automatic zooming features, which we will talk about in few minutes.

I also make use of generics for the markers and paths lists. The last member is the Google API Key, don't forget to get yours!

clip_image002[9] Collapse

#region Instance members

/// <summary>

/// Indicates if automatic center is enabled

/// (available if at least one marker present)

/// </summary>

private bool _automaticCenter;

/// <summary>

/// Indicates if automatic zoom level is enabled

/// (available if at least one marker present)

/// </summary>

private bool _automaticZoomLevel;

/// <summary>

///Center (required if markers or paths not present)

/// </summary>

private GCoordinate _center;

/// <summary>

/// ZoomLevel (required if markers or paths not present)

/// </summary>

private uint? _zoomLevel = null;

/// <summary>

/// Image format (optional)

/// </summary>

private MapImageFormat? _imageFormat = null;

/// <summary>

/// Map type (optional)

/// </summary>

private MapType? _type = null;

/// <summary>

/// List of markers

/// </summary>

private List<GMarker> _markers = new List<GMarker>();

/// <summary>

/// List of paths

/// </summary>

private List<GPath> _paths = new List<GPath>();

/// <summary>

/// Google Base API Key (required)

/// Go to http://code.google.com/apis/base/signup.html to get yours

/// (it's free and doesn't require any subscription)

/// </summary>

private string _key;

#endregion

It’s now time to declare our public accessors.

Since we are making a WebControl, you'll notice that most of them have a Bindable attribute in order to become ASP.NET attributes (i.e. you can define them directly in the tag that you may declare in the ASPX page) and to be available in the design mode.

Don't worry if you don't see the Center, Markers and Paths here, a specific region will covers them since they are now accessible as children in the ASPX page.

Accessors are very interesting because they encapsulate the way they manage the value you pass or retrieve to them: have a look at the zoom level (ZoomLevel), and the setter (which becomes a method during the compilation) checks bounds with the help of the enumerator. By contrast, automatic center and automatic zoom accessors alter the return value through the getter (also transformed to a method by the compiler).

The last interesting thing in this part is about the ImageUrl accessor. I make use of the new keyword as a modifier in order to redefine the base class’ImageUrl accessor. Here, I wanted ImageUrl to be readable but not settable (because it’s built by our class before rendering), so I only defined a getter returning the value of the base class accessor (base keyword):

clip_image002[10] Collapse

#region Accessors

/// <summary>

/// ZoomLevel (required if markers or paths not present)

/// </summary>

[Bindable(true)]

[Category("Appearance")]

[DefaultValue("MapZoomLevel.Mini")]

[Localizable(false)]

public uint? ZoomLevel

{

get { return _zoomLevel; }

set

{

// Check zoom bounds

if (value < (uint)MapZoomLevel.Mini) { value = (uint)MapZoomLevel.Mini; }

else if (value > (uint)MapZoomLevel.Maxi) { value = (uint)MapZoomLevel.Maxi; }

// Apply new zoom

_zoomLevel = value;

}

}

/// <summary>

/// Image format (optional)

/// </summary>

[Bindable(true)]

[Category("Appearance")]

[DefaultValue("PNG")]

[Localizable(false)]

public MapImageFormat? ImageFormat

{

get { return _imageFormat; }

set { _imageFormat = value; }

}

/// <summary>

/// Map type (optional)

/// </summary>

[Bindable(true)]

[Category("Appearance")]

[DefaultValue("RoadMap")]

[Localizable(false)]

public MapType? Type

{

get { return _type; }

set { _type = value; }

}

/// <summary>

/// Google Base API Key (required)

/// Go to http://code.google.com/apis/base/signup.html to get yours

/// (it's free and doesn't require any subscription)

/// </summary>

[Bindable(true)]

[Category("Google")]

[DefaultValue("")]

[Localizable(false)]

public string Key

{

get { return _key; }

set { _key = value; }

}

/// <summary>

/// Indicates if automatic center is enabled (available if at least one marker present)

/// </summary>

[Bindable(true)]

[Category("Appearance")]

[DefaultValue("")]

[Localizable(false)]

public bool AutomaticCenter

{

get { return _automaticCenter && IsAutomaticAvailable; }

set { _automaticCenter = value; }

}

/// <summary>

/// Indicates if automatic zoom level is enabled

/// (available if at least one marker present)

/// </summary>

[Bindable(true)]

[Category("Appearance")]

[DefaultValue("")]

[Localizable(false)]

public bool AutomaticZoomLevel

{

get { return _automaticZoomLevel && IsAutomaticAvailable; }

set { _automaticZoomLevel = value; }

}

/// <summary>

/// Redefine picture's ImageUrl to avoid any modification

/// (since ImageUrl is built by the webcontrol)

/// </summary>

public new string ImageUrl

{

get { return base.ImageUrl; }

}

/// <summary>

/// Indicates whether automatic center and levels are available

/// </summary>

public bool IsAutomaticAvailable

{

get { return (Markers.Count > 0 || Paths.Count > 0); }

}

#endregion

Here are the accessors that you can use directly in the ASPX page, the DesignerSerializationVisibility attribute for each of them make (according to MSDN) the code generator produce code for the contents of the object, rather than for the object itself (so you can access them in the ASPX page):

clip_image002[11] Collapse

#region Childs

/// <summary>

/// Center (required if markers or paths not present)

/// </summary>

[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]

public GCoordinate Center

{

get

{

if (_center == null)

{

_center = new GCoordinate();

}

return _center;

}

}

/// <summary>

/// Markers (optional)

/// </summary>

[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]

public List<GMarker> Markers

{

get { return _markers; }

}

/// <summary>

/// Paths (optional)

/// </summary>

[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]

public List<GPath> Paths

{

get { return _paths; }

}

#endregion

Now, since we have all the needed members and attributes to complete our aim, it’s time to write the BuildUrl method that will produce the final URL, according to the Google Static Map API.

Serialization output: http://maps.google.com/staticmap?parameters.

clip_image002[12] Collapse

#region Utilities

/// <summary>

/// Build up the URL used to request the generated picture

/// Format : http://maps.google.com/staticmap?parameters

/// </summary>

/// <returns></returns>

private string BuildUrl()

{

// Base URL (change if needed)

string url = "http://maps.google.com/staticmap?";

List<string> parameters = new List<string>();

// Center (required if markers or paths not present)

if (!AutomaticCenter)

{

parameters.Add("center=" + Center.SerializeAsQueryString());

}

// ZoomLevel (required if markers or paths not present)

if (!AutomaticZoomLevel)

{

parameters.Add("zoom=" + ZoomLevel);

}

// Image size

parameters.Add(string.Format("size={0}x{1}",

(int)Width.Value, (int)Height.Value));

// Image format (optional)

if (ImageFormat != null)

{

parameters.Add("format=" + Enum.GetName(typeof(MapImageFormat),

ImageFormat).ToLower());

}

// Map type (optional)

if (Type != null)

{

parameters.Add("maptype=" + Enum.GetName(typeof(MapType), Type).ToLower());

}

// Markers (optional)

if (Markers.Count > 0)

{

// Gather markers in a string list

List<string> markers = new List<string>();

foreach (GMarker poi in Markers)

{

markers.Add(poi.SerializeAsQueryString());

}

// Add them as a parameter

parameters.Add(string.Format

("markers={0}", string.Join("|", markers.ToArray())));

}

// Paths (optional)

if (Paths.Count > 0)

{

foreach (GPath path in Paths)

{

// Add them as a parameter

parameters.Add(path.SerializeAsQueryString());

}

}

// ZoomLevel (required if markers or paths not present)

if (Key != string.Empty)

{

parameters.Add("key=" + Key);

}

// Build up and returns final URL

return url + string.Join("&", parameters.ToArray());

}

#endregion

The next logical step is to set the ImageUrl accessor. The best moment to do this is just before the rendering, so we just have to overload the OnPreRendervirtual method to achieve this:

clip_image002[13] Collapse

#region Methods

/// <summary>

/// Build the image url according to parameters and added points

/// </summary>

/// <param name="e"></param>

protected override void OnPreRender(EventArgs e)

{

base.OnPreRender(e);

// Set Image Url before rendering

base.ImageUrl = BuildUrl();

}

#endregion

OK, our work is looking good now, but we have missed something… How will the state of our control be maintained after a postback? Obviously with theViewState, so here is the way to do this: we must overload the LoadViewState and SaveViewState virtual methods to, respectively load ViewStatevalues in the control and save the control values in the ViewState (you can also directly save or load some values on demand, by using accessors):

clip_image002[14] Collapse

/// <summary>

/// Load viewstate values

/// </summary>

/// <param name="savedState"></param>

protected override void LoadViewState(object savedState)

{

base.LoadViewState(savedState);

_center = (GCoordinate)ViewState["Center"];

ZoomLevel = (uint)ViewState["ZoomLevel"];

Type = (MapType)ViewState["MapType"];

Markers.AddRange(new List<GMarker>((GMarker[])ViewState["Markers"]));

Paths.AddRange(new List<GPath>((GPath[])ViewState["Paths"]));

Key = (string)ViewState["Key"];

AutomaticCenter = (bool)ViewState["AutomaticCenter"];

AutomaticZoomLevel = (bool)ViewState["AutomaticZoomLevel"];

}

/// <summary>

/// Save viewstate values

/// </summary>

/// <returns></returns>

protected override object SaveViewState()

{

ViewState["Center"] = _center;

ViewState["ZoomLevel"] = ZoomLevel;

ViewState["MapType"] = Type;

ViewState["Markers"] = Markers.ToArray();

ViewState["Paths"] = Paths.ToArray();

ViewState["Key"] = Key;

ViewState["AutomaticCenter"] = AutomaticCenter;

ViewState["AutomaticZoomLevel"] = AutomaticZoomLevel;

return base.SaveViewState();

}

The only tricky thing was to save and load the markers and paths list based on a generic class. It may be a better way to do this, but I convert the list into an array and get it back to a list during the ViewState loading phase.

Using the WebControl in an ASPX Page

If you're a Web Designer or not so close to ASP.NET programming, you can now widely use the WebControl directly in an ASPX page.

First, you need to register the WebControl in your page:

clip_image002[15] Collapse

<%@ Register TagPrefix="asp" Assembly="GStaticMapTest"

Namespace="Initia.Google.GStaticMap" %>

Then, you can show a simple map like this (don't forget runat="server" and the center coordinate, since there are no markers and paths):

clip_image002[16] Collapse

<asp:GStaticMap ID="gsmTestMap" AutomaticZoomLevel="true" AutomaticCenter="true"

Width="320" Height="200" Type="RoadMap" ZoomLevel="12" Key="put_yours_here"

runat="server">

<Center Latitude="45.759723" Longitude="4.842223"></Center>

</asp:GStaticMap>

As needed, you may add one or several markers (the center is optional if you use automatic center):

clip_image002[17] Collapse

<asp:GStaticMap ID="gsmTestMap" AutomaticZoomLevel="true" AutomaticCenter="true"

Width="320" Height="200" Type="RoadMap" ZoomLevel="12"

Key="put_yours_here" runat="server">

<Center Latitude="45.759723" Longitude="4.842223"></Center>

<Markers>

<asp:GMarker Latitude="45.859723" Longitude="4.842223"

Letter="a" Color="Red">

</asp:GMarker>

<asp:GMarker Latitude="45.759723" Longitude="5.042223"

Letter="b" Color="Green">

</asp:GMarker>

<asp:GMarker Latitude="45.659723" Longitude="4.842223"

Letter="c" Color="Blue">

</asp:GMarker>

<asp:GMarker Latitude="45.759723" Longitude="4.642223"

Color="Green">

</asp:GMarker>

</Markers>

</asp:GStaticMap>

Finally, you may add one or several paths on your map:

clip_image002[18] Collapse

<asp:GStaticMap ID="gsmTestMap" AutomaticZoomLevel="true" AutomaticCenter="true"

Width="320" Height="200" Type="RoadMap" ZoomLevel="12"

Key="put_yours_here" runat="server">

<Center Latitude="45.759723" Longitude="4.842223"></Center>

<Markers>

<asp:GMarker Latitude="45.859723"

Longitude="4.842223" Letter="a" Color="Red">

</asp:GMarker>

<asp:GMarker Latitude="45.759723"

Longitude="5.042223" Letter="b" Color="Green">

</asp:GMarker>

<asp:GMarker Latitude="45.659723"

Longitude="4.842223" Letter="c" Color="Blue">

</asp:GMarker>

<asp:GMarker Latitude="45.759723"

Longitude="4.642223" Color="Green">

</asp:GMarker>

</Markers>

<Paths>

<asp:GPath Color="blue" Opacity="255" Weight="5">

<Points>

<asp:GCoordinate Latitude="45.859723" Longitude="4.842223">

</asp:GCoordinate>

<asp:GCoordinate Latitude="45.759723" Longitude="5.042223">

</asp:GCoordinate>

<asp:GCoordinate Latitude="45.659723" Longitude="4.842223">

</asp:GCoordinate>

</Points>

</asp:GPath>

<asp:GPath Color="red" Opacity="128" Weight="5">

<Points>

<asp:GCoordinate Latitude="45.659723" Longitude="4.842223">

</asp:GCoordinate>

<asp:GCoordinate Latitude="45.759723" Longitude="4.642223">

</asp:GCoordinate>

<asp:GCoordinate Latitude="45.859723" Longitude="4.842223">

</asp:GCoordinate>

</Points>

</asp:GPath>

</Paths>

</asp:GStaticMap>

Using the WebControl in the Code-behind

If you love coding or have to programmatically manage static maps, you can use code-behind to fit your needs.

The sample project included with this article shows you how to do this, here is some explanation about the basics.

First, create an ASPX page and register the newly created WebControl:

clip_image002[19] Collapse

<%@ Register TagPrefix="asp" Assembly="GStaticMapTest"

Namespace="Initia.Google.GStaticMap" %>

Now, "instantiate" the WebControl (this reference is automatically added in the designer if the ASPX page is right) where you want the static map to render:

clip_image002[20] Collapse

<asp:GStaticMap ID="gsmTestMap" runat="server"></asp:GStaticMap>

At this point, you can set some attributes like automatic center, map type, width, height… and you must set the Google API Key (beware: Google will serve you 1000 times the same static map per day and per IP address, you should use cache to avoid problems if you have some traffic !)

To avoid prefixing GStaticMap components in the code-behind, use the following namespace:

clip_image002[21] Collapse

using Initia.Google.GStaticMap;

Then, you can show a simple map like this (don't forget the center coordinate, since there are no markers and paths):

clip_image002[22] Collapse

// Initialize map size

gsmTestMap.Width = int.Parse(tbMapWidth.Text);

gsmTestMap.Height = int.Parse(tbMapHeight.Text);

// Set map type to Mobile

gsmTestMap.Type = GStaticMapType.Mobile;

// Center the static map to my town: Lyon, FRANCE

gsmTestMap.Center.SetCoordinate(45.759722, 4.842222);

gsmTestMap.Key = "put_yours_here";

// Set the zoom level

gsmTestMap.ZoomLevel = 8;

As needed, you may add one or several markers (the center becomes optional if you use automatic center):

clip_image002[23] Collapse

gsmTestMap.Markers.Add(new GMarker(45.759722, 4.842222, GMarkerColor.Blue, 'd');

Finally, you may add one or several paths on your map like this:

clip_image002[24] Collapse

// Trace a path from Lyon, FRANCE (45° 45' 35? N, 4° 50' 32? E) to Paris,

FRANCE (48° 51' 23.68? N,

// 2° 21' 6.58? E)

GPath fromLyonToParis = new GPath(KnownColor.Red, 5, 255);

fromLyonToParis.Points.Add(new GCoordinate(45.759723, 4.842223));

fromLyonToParis.Points.Add(new GCoordinate(48.856578, 2.351828));

gsmTestMap.Paths.Add(fromLyonToParis);

Points of Interest

This WebControl now covers all features of discontinued MapData WebControl, using the official static maps API from Google.

With this article, you learned how to:

  • build a useful custom WebControl with attributes and children
  • manage the ViewState
  • use the Google Static Maps API

Known Limitations

  • Missing visual designer
  • Missing icon for the toolbox

About Me

Developers house is a blog for posting technical articles in different technology like Microsft, Java, Oracle ..etc Microsoft technology includes c#,VB.net,ASP.net,Ajax,SilverLight,TFS,VS.NET 2003,2005,2008,2010 , SQL Server 2000, 2005 , Expression Blend , ...etc I hope it is helpful for all of you and if you are interested to post articles on it, only send me at ahmad.eed@gmail.com