/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is WCPierce Web Controls.
*
* The Initial Developer of the Original Code is William C. Pierce.
* Portions created by the Initial Developer are Copyright (C) 2005
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Ren� Strauss
* JL Tejeda
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
using System;
using System.ComponentModel;
using System.Collections;
using System.IO;
using System.Web;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using System.Text;
using System.Xml.Xsl;
[assembly:TagPrefix("WCPierce.Web.UI.WebControls", "wcp")]
namespace WCPierce.Web.UI.WebControls
{
/// <summary>
/// Creates a Google Map that the developer can interact with programatically
/// using .Net languages
///
/// 2005-09-08
/// + Corrected OnDataBinding, had my lat/lng values reversed
/// 2005-09-10
/// + Added the ZoomLevel hidden input field for posting back current zoom level
/// + Cleaned up public Properties to use ViewState more consistently
/// + Removed DataGPointField, dunno what I was thinking
/// 2005-09-21
/// + Corrected problem with use of GMap in a composite control. Thanks to
/// Ren� Strauss.
/// 2005-09-28
/// + Cleaned up postback value handling.
/// + Fixed bug with numbers not rendering properly causing the GMap not to propertly
/// initialize. Thanks to JL Tejeda.
/// 2005-10-05
/// + Corrected the closing form tag hack in the Render method. The control now
/// renders in a more proper manner.
/// + Added friendlyControlId to the GetXsltArguments
/// 2005-10-12
/// + Added two new events ClientLoad and MapTypeChanged. ClientLoad will fire after
/// the map has completely loaded. This will give developers to take action based
/// on the Center, Span, Bounds, etc. Map Type Changed will fire whenever the user
/// changes the Map Type from standard to satellite/hybrid, etc.
/// + Made some (hopefully) non breaking changes to the way events are handled to be more
/// closely inline with ASP.Net 2.0 ICallbackEventHandler style.
/// + Added another hidden field to track the Map Type upon postback.
/// </summary>
[ToolboxData("<{0}:GMap runat=server></{0}:GMap>")]
public class GMap : WebControl, IPostBackEventHandler, IPostBackDataHandler
{
#region Member Variables
/// <summary>
/// This is a data representation of the GMap to be displayed. Based very
/// loosely on Google's GVPage
/// </summary>
private GXPage _gxpage;
/// <summary>
/// Used to store any "method" operations on this GMap. Builds the proper
/// javascript to implement the method calls client side
/// </summary>
private StringBuilder initJs = new StringBuilder();
private static readonly string _GoogleApiKey = "GoogleApiKey";
private string _scriptPath = "http://maps.google.com/maps?file=api&v=1&key=";
private string _scriptFolderPath = String.Empty;
private string _googleApiKey = String.Empty;
private string _friendlyUniqueId = String.Empty;
private string _divId = String.Empty;
private string _jsId = String.Empty;
private const string _centerLatLngField = "_CenterLatLng";
private GPoint _centerLatLng;
private const string _spanLatLngField = "_SpanLatLng";
private GSize _spanLatLng;
private const string _boundsLatLngField = "_BoundsLatLng";
private GBounds _boundsLatLng;
private GMapType _supportedMapTypes = GMapType.G_MAP_TYPE & GMapType.G_SATELLITE_TYPE;
private const string _zoomLevelField = "_ZoomLevel";
private int _zoomLevel = 1;
private object _dataSource = null;
private const string _mapTypeField = "_MapType";
#endregion
#region Public Properties
/// <summary>
/// Initializes a new instance of the GMap control, based on the Div tag
/// </summary>
public GMap() : base(HtmlTextWriterTag.Div)
{
_gxpage = new GXPage();
_centerLatLng = new GPoint();
_spanLatLng = new GSize();
_boundsLatLng = new GBounds();
}
/// <summary>
/// The path to the Google Maps javascript API
/// </summary>
public string GMapScriptPath
{
get { return _scriptPath + this.GoogleApiKey; }
}
/// <summary>
/// The (relative) path to the developers script directory. This method
/// first checks to see if the developer has already set the path. If not
/// the web.config file is checked via a call to Utilities.ScriptFolderPath
/// </summary>
public string ScriptFolderPath
{
get
{
if( _scriptFolderPath != String.Empty )
return ResolveUrl(_scriptFolderPath);
else
return ResolveUrl(Utilities.ScriptFolderPath);
}
set { _scriptFolderPath = value; }
}
/// <summary>
/// The name of the accompanying Javascript file
/// </summary>
public static string ScriptName
{
get { return "GMapX.js"; }
}
/// <summary>
/// The Google API key assigned by Google. This method
/// first checks to see if the developer has already set the key. If not
/// the web.config file is checked.
/// </summary>
public string GoogleApiKey
{
get
{
if( _googleApiKey != String.Empty )
return _googleApiKey;
string key = System.Configuration.ConfigurationSettings.AppSettings[GMap._GoogleApiKey];
if( key == null )
key = String.Empty;
return key;
}
set { _googleApiKey = value; }
}
/// <summary>
/// A strongly-typed ArrayList that holds map Overlays i.e. GMarker and GPolyline
/// </summary>
public GOverlays Overlays
{
get { return _gxpage.Overlays; }
}
/// <summary>
/// A strongly-typed ArrayList that holds map Icons
/// </summary>
public GIcons Icons
{
get { return _gxpage.Icons; }
}
/// <summary>
/// If true, enables Client Callbackes for GMap events. --freakin' sweet feature--
/// </summary>
public bool EnableClientCallBacks
{
get
{
object o = this.ViewState["EnableClientCallBacks"];
if (o != null)
{
return (bool)o;
}
return false;
}
set
{
this.ViewState["EnableClientCallBacks"] = value;
}
}
/// <summary>
/// The Center lat/lng coordinate of the map
/// </summary>
public GPoint CenterLatLng
{
get { return _centerLatLng; }
}
/// <summary>
/// The Span distance of the map in lat/lng ticks
/// </summary>
public GSize SpanLatLng
{
get { return _spanLatLng; }
}
/// <summary>
/// The bounding box of the map
/// </summary>
public GBounds BoundsLatLng
{
get { return _boundsLatLng; }
}
/// <summary>
/// Enables dynamic dragging (enabled by default)
/// </summary>
public bool EnableDragging
{
get
{
object o = this.ViewState["EnableDragging"];
if (o != null)
{
return (bool)o;
}
return true;
}
set
{
this.ViewState["EnableDragging"] = value;
}
}
/// <summary>
/// Enables the popup info windows on this map (enabled by default)
/// </summary>
public bool EnableInfoWindow
{
get
{
object o = this.ViewState["EnableInfoWindow"];
if (o != null)
{
return (bool)o;
}
return true;
}
set
{
this.ViewState["EnableInfoWindow"] = value;
}
}
/// <summary>
/// Returns the integer zoom level of the map
/// </summary>
public int ZoomLevel
{
get { return _zoomLevel; }
set { _zoomLevel = value; }
}
/// <summary>
/// Returns the map type currently in use
/// </summary>
public GMapType MapType
{
get
{
object o = this.ViewState["MapType"];
if (o != null)
{
return (GMapType)o;
}
return GMapType.G_MAP_TYPE;
}
set
{
this.ViewState["MapType"] = value;
}
}
/// <summary>
/// Returns the map types supported by this map (e.g., G_MAP_TYPE & G_SATELLITE_TYPE)
/// </summary>
public GMapType SupportedMapTypes
{
get { return _supportedMapTypes; }
set { _supportedMapTypes = value; }
}
/// <summary>
/// For use with databinding.
/// </summary>
[Bindable(true), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), DefaultValue((string) null)]
public virtual object DataSource
{
get
{
return _dataSource;
}
set
{
if (((value != null) && !(value is IListSource)) && !(value is IEnumerable))
{
throw new ArgumentException("Invalid_DataSource_Type: " + this.ID);
}
_dataSource = value;
}
}
/// <summary>
/// For use with databinding
/// </summary>
public virtual string DataMarkerIdField
{
get
{
object o = this.ViewState["DataMarkerIdField"];
if (o != null)
{
return (string)o;
}
return string.Empty;
}
set
{
this.ViewState["DataMarkerIdField"] = value;
}
}
/// <summary>
/// For use with databinding
/// </summary>
public virtual string DataIconIdField
{
get
{
object o = this.ViewState["DataIconIdField"];
if (o != null)
{
return (string)o;
}
return string.Empty;
}
set
{
this.ViewState["DataIconIdField"] = value;
}
}
/// <summary>
/// For use with databinding.
/// </summary>
public virtual string DataLatitudeField
{
get
{
object o = this.ViewState["DataLatitudeField"];
if (o != null)
{
return (string)o;
}
return string.Empty;
}
set
{
this.ViewState["DataLatitudeField"] = value;
}
}
/// <summary>
/// For use with databinding.
/// </summary>
public virtual string DataLongitudeField
{
get
{
object o = this.ViewState["DataLongitudeField"];
if (o != null)
{
return (string)o;
}
return string.Empty;
}
set
{
this.ViewState["DataLongitudeField"] = value;
}
}
/// <summary>
/// For use with databinding.
/// </summary>
[DefaultValue("")]
public virtual string DataMember
{
get
{
object o = this.ViewState["DataMember"];
if (o != null)
{
return (string)o;
}
return string.Empty;
}
set
{
this.ViewState["DataMember"] = value;
}
}
#endregion
#region Public Methods
/// <summary>
/// Creates the proper Javascript code to center and zoom the map at the
/// given LatLng/ZoomLevel. This is added to the "iniJs" which is
/// rendered with the control. Also useful for Client CallBacks
/// </summary>
/// <param name="LatLng">The Latitude/Longitude coordinate to center at</param>
/// <param name="ZoomLevel">The Zoom Level to center to</param>
/// <returns>A string of client side javascript to implement the centerAndZoom</returns>
public string CenterAndZoom( GPoint LatLng, int ZoomLevel )
{
string str = String.Format(Utilities.UsCulture, "{0}.centerAndZoom(new GPoint({1}, {2}), {3});", this.JsId, LatLng.X, LatLng.Y, ZoomLevel);
initJs.Append(str);
return str;
}
/// <summary>
/// Creates the proper Javascript code to zoom the map at the
/// given ZoomLevel. This is added to the "iniJs" which is
/// rendered with the control. Also useful for Client CallBacks
/// </summary>
/// <param name="ZoomLevel">The Zoom Level to apply to the map</param>
/// <returns>A string of client side javascript to implement the zoomTo</returns>
public string ZoomTo(int ZoomLevel )
{
string str = String.Format(Utilities.UsCulture, "{0}.zoomTo({1});", this.JsId, ZoomLevel);
initJs.Append(str);
return str;
}
/// <summary>
/// Creates the proper Javascript code to center the map at the
/// given Latitude/Longitude coordinate. This is added to the "iniJs" which
/// is rendered with the control. Also useful for Client CallBacks
/// </summary>
/// <param name="LatLng">The Latitude/Longitude coordinate to center at</param>
/// <returns>A string of client side javascript to implement the centerAtLatLng</returns>
public string CenterAtLatLng(GPoint LatLng)
{
string str = String.Format(Utilities.UsCulture, "{0}.centerAtLatLng(new GPoint({1},{2}));", this.JsId, LatLng.X, LatLng.Y);
initJs.Append(str);
return str;
}
/// <summary>
/// Creates the proper Javascript code to center/pan the map to the
/// given Latitude/Longitude coordinate. This is added to the "iniJs" which
/// is rendered with the control. Also useful for Client CallBacks
/// </summary>
/// <param name="LatLng">The Latitude/Longitude coordinate to center/pan to</param>
/// <returns>A string of client side javascript to implement the recenterOrPanToLatLng</returns>
public string RecenterOrPanToLatLng(GPoint LatLng)
{
string str = String.Format(Utilities.UsCulture, "{0}.recenterOrPanToLatLng({1},{2});", this.JsId, LatLng.X, LatLng.Y);
initJs.Append(str);
return str;
}
/// <summary>
/// Displays the info window with the given HTML content at the given
/// lat/lng point. htmlElem should be an HTML DOM element. If
/// pixelOffset (GSize) is given, we offset the info window by that
/// number of pixels, which lets users place info windows above markers
/// and other overlays. If onOpenFn is given, we call the function when
/// the window is displayed, and we call onCloseFn when the window is
/// closed.
/// </summary>
/// <param name="LatLng"></param>
/// <param name="HtmlElem"></param>
/// <returns></returns>
public string OpenInfoWindow(GPoint LatLng, string HtmlElem)
{
string str = String.Format(Utilities.UsCulture, "{0}.openInfoWindow(new GPoint({1},{2}), {3});", this.JsId, LatLng.X, LatLng.Y, HtmlElem);
initJs.Append(str);
return str;
}
/// <summary>
/// Like openInfoWindow, but takes an HTML string rather than an HTML DOM
/// element
/// </summary>
/// <param name="LatLng"></param>
/// <param name="HtmlStr"></param>
/// <param name="PixelOffset"></param>
/// <returns></returns>
public string OpenInfoWindowHtml(GPoint LatLng, string HtmlStr, GSize PixelOffset)
{
string str = String.Format(Utilities.UsCulture, "{0}.openInfoWindowHtml(new GPoint({1},{2}), '{3}', new GSize({4},{5}));", this.JsId, LatLng.X, LatLng.Y, HtmlStr, PixelOffset.Width, PixelOffset.Height);
initJs.Append(str);
return str;
}
/// <summary>
///
/// </summary>
/// <param name="LatLng"></param>
/// <param name="HtmlStr"></param>
/// <returns></returns>
public string OpenInfoWindowHtml(GPoint LatLng, string HtmlStr)
{
return OpenInfoWindowHtml(LatLng, HtmlStr, new GSize(0, 0));
}
/// <summary>
/// Like openInfoWindow, but takes an XML element and the URI of an XSLT
/// document to produce the content of the info window. The first time a
/// URI is given, it is downloaded with GXmlHttp and subsequently cached.
/// </summary>
/// <param name="LatLng"></param>
/// <param name="Xml"></param>
/// <param name="XsltUri"></param>
/// <param name="PixelOffset"></param>
/// <returns></returns>
public string OpenInfoWindowXslt(GPoint LatLng, string Xml, Uri XsltUri, GSize PixelOffset)
{
string str = String.Format(Utilities.UsCulture, "{0}.openInfoWindowXslt(new GPoint({1},{2}), GXml.parse('{3}'), '{4}', new GSize({5},{6}));", this.JsId, LatLng.X, LatLng.Y, Xml, XsltUri.AbsoluteUri, PixelOffset.Width, PixelOffset.Height);
initJs.Append(str);
return str;
}
/// <summary>
///
/// </summary>
/// <param name="LatLng"></param>
/// <param name="Xml"></param>
/// <param name="XsltUri"></param>
/// <returns></returns>
public string OpenInfoWindowXslt(GPoint LatLng, string Xml, Uri XsltUri)
{
return OpenInfoWindowXslt(LatLng, Xml, XsltUri, new GSize(0, 0));
}
/// <summary>
/// Shows a blowup of the map at the given lat/lng GPoint. We use a default
/// zoom level of 1 and the current map type if the zoomLevel and mapType
/// parameters are not given.
/// </summary>
/// <param name="LatLng"></param>
/// <param name="ZoomLevel"></param>
/// <param name="MapType"></param>
/// <param name="PixelOffset"></param>
/// <returns></returns>
public string ShowMapBlowup(GPoint LatLng, int ZoomLevel, GMapType MapType, GSize PixelOffset)
{
string str = String.Format(Utilities.UsCulture, "{0}.showMapBlowup(new GPoint({1},{2}), {3}, {4}, new GSize({5},{6}));", this.JsId, LatLng.X, LatLng.Y, ZoomLevel, MapType, PixelOffset.Width, PixelOffset.Height);
initJs.Append(str);
return str;
}
/// <summary>
///
/// </summary>
/// <param name="LatLng"></param>
/// <returns></returns>
public string ShowMapBlowup(GPoint LatLng)
{
return ShowMapBlowup(LatLng, 1, GMapType.G_MAP_TYPE, new GSize(0, 0));
}
/// <summary>
/// Creates the proper Javascript code to clear all map overlays. This is
/// added to the "iniJs" which is rendered with the control. Also useful
/// for Client CallBacks
/// </summary>
/// <returns>A string of client side javascript to implement the clearOverlays</returns>
public string ClearOverlays()
{
string str = String.Format(Utilities.UsCulture, "{0}.clearOverlays();", this.JsId);
initJs.Append(str);
return str;
}
/// <summary>
/// Adds a GControl to the map
/// </summary>
/// <param name="Control">The GControl to add</param>
public void AddControl(GControl Control)
{
_gxpage.Controls.Add(Control);
}
#endregion
#region Private Properties
/// <summary>
/// A javascript friendly version of the controls unique id
/// </summary>
private string FriendlyUniqueId
{
get
{
if( _friendlyUniqueId == String.Empty )
_friendlyUniqueId = this.UniqueID.Replace(":", "_");
return _friendlyUniqueId;
}
}
/// <summary>
/// The unique id for the div panel of the GMap
/// </summary>
private string DivId
{
get
{
if( _divId == String.Empty )
_divId = this.UniqueID + "_Div";
return _divId;
}
}
/// <summary>
/// The unique javascript id for the GMap
/// </summary>
private string JsId
{
get
{
if( _jsId == String.Empty )
_jsId = this.FriendlyUniqueId + "_Js";
return _jsId;
}
}
#endregion
#region Overrides
/// <summary>
/// Creates the hidden input fields used to pass the map state back to the
/// server. One field each for the Center, Span, and Bounds fields
/// </summary>
protected override void CreateChildControls()
{
base.CreateChildControls();
string id = this.ID;
HtmlInputHidden centerLatLng = new HtmlInputHidden();
centerLatLng.ID = id + _centerLatLngField;
base.Controls.Add(centerLatLng);
HtmlInputHidden spanLatLng = new HtmlInputHidden();
spanLatLng.ID = id + _spanLatLngField;
base.Controls.Add(spanLatLng);
HtmlInputHidden boundsLatLng = new HtmlInputHidden();
boundsLatLng.ID = id + _boundsLatLngField;
base.Controls.Add(boundsLatLng);
HtmlInputHidden zoomLevel = new HtmlInputHidden();
zoomLevel.ID = id + _zoomLevelField;
base.Controls.Add(zoomLevel);
HtmlInputHidden mapType = new HtmlInputHidden();
mapType.ID = id + _mapTypeField;
base.Controls.Add(mapType);
}
/// <summary>
/// I was attempting to properly create a custom control, but ended up doing
/// a little hacking
/// </summary>
/// <param name="writer"></param>
protected override void AddAttributesToRender(HtmlTextWriter writer)
{
if (this.Page != null)
{
this.Page.VerifyRenderingInServerForm(this);
}
writer.AddAttribute(HtmlTextWriterAttribute.Name, this.DivId);
writer.AddAttribute(HtmlTextWriterAttribute.Id, this.DivId);
// base.AddAttributesToRender automatically renders the Id field as the
// Id of the control unless the control Id is null. We don't want the
// div tag's id to be the controls id. We want it to be {controlId}_Div.
// So we need to do a little hack.
string tId = base.ID;
base.ID = null;
base.AddAttributesToRender(writer);
base.ID = tId;
}
/// <summary>
/// XML ok, XSL bad. Thanks to our GXPage and some XSL, the render method
/// is nice and short. See GMap.xsl for the full rendering goodness.
/// </summary>
/// <param name="output"></param>
protected override void Render(HtmlTextWriter output)
{
base.Render(output);
StringBuilder sb = new StringBuilder();
// Transform our GXPage to javascript using our GMap.xsl stylesheet
string path = Page.Server.MapPath(this.ScriptFolderPath + "/" + "GMap.xsl");
XsltArgumentList xal = GetXsltArguments();
sb.Append(GXslt.Transform(_gxpage, path, xal));
// Register our script for output to the page.
Page.RegisterStartupScript(this.UniqueID, sb.ToString());
}
/// <summary>
/// I overrode this method so that Developers weren't constantly
/// reading/writing to the ViewState when accessing the map's GXPage.
/// Not sure if this is proper/recommended?
/// </summary>
/// <returns></returns>
protected override object SaveViewState()
{
ViewState["GXPage"] = _gxpage;
return base.SaveViewState();
}
/// <summary>
/// I overrode this method so that Developers weren't constantly
/// reading/writing to the ViewState when accessing the map's GXPage.
/// Not sure if this is proper/recommended?
/// </summary>
/// <param name="savedState"></param>
protected override void LoadViewState(object savedState)
{
base.LoadViewState(savedState);
_gxpage = ViewState["GXPage"] as GXPage;
}
/// <summary>
/// Register our common scripts and do default PreRendering.
/// </summary>
/// <param name="e"></param>
protected override void OnPreRender(EventArgs e)
{
base.OnPreRender(e);
// Required for us to capture postback data
Page.RegisterRequiresPostBack(this);
// Register our standard scripts
this._RegisterCommonScripts();
}
/// <summary>
/// Good old databinding. This will check the bound DataSource for a Latitide
/// field and a Longitude field. A marker will be added for each entry using
/// the default icon. --freakin' sweet feature--
/// </summary>
/// <param name="e"></param>
protected override void OnDataBinding(EventArgs e)
{
base.OnDataBinding (e);
_gxpage.Overlays.Clear();
IEnumerable ie = DataSourceHelper.GetResolvedDataSource(this.DataSource, this.DataMember);
if( ie != null )
{
string latField = DataLatitudeField;
string lngField = DataLongitudeField;
string mIdField = DataMarkerIdField;
string iIdField = DataIconIdField;
foreach( object o in ie )
{
float lat = Convert.ToSingle(DataBinder.GetPropertyValue(o, latField), Utilities.UsCulture);
float lng = Convert.ToSingle(DataBinder.GetPropertyValue(o, lngField), Utilities.UsCulture);
GPoint gPoint = new GPoint(lng, lat);
// Marker Id is not required. If the developer didn't specify it, we just ignore it
string mId = String.Empty;
try
{
mId = DataBinder.GetPropertyValue(o, mIdField, "{0}");
}
catch( Exception ex )
{
if( !(ex is ArgumentNullException) )
throw;
}
// IconId is not required. If the developer didn't specify it, we just ignore it
string iId = String.Empty;
try
{
iId = DataBinder.GetPropertyValue(o, iIdField, "{0}");
}
catch( Exception ex )
{
if( !(ex is ArgumentNullException) )
throw;
}
GMarker gm = new GMarker(gPoint, mId, iId);
_gxpage.Overlays.Add(gm);
}
}
}
#endregion
#region Helper Methods
/// <summary>
/// Add a reference to the JavaScript, but only once per page. The order
/// in which these scripts are registered is important
/// </summary>
private void _RegisterCommonScripts()
{
// Register the GoogleMaps API script
if(!this.Page.IsClientScriptBlockRegistered("GMap"))
{
StringBuilder script = new StringBuilder();
script.AppendFormat("<script type='text/javascript' src='{0}'></script>", this.GMapScriptPath);
this.Page.RegisterClientScriptBlock("GMap", script.ToString());
}
// Register our GMapX script
if(!this.Page.IsClientScriptBlockRegistered(GMap.ScriptName))
{
StringBuilder script = new StringBuilder();
script.AppendFormat("<script type='text/javascript' src='{0}/{1}'></script>", this.ScriptFolderPath, GMap.ScriptName);
this.Page.RegisterClientScriptBlock(GMap.ScriptName, script.ToString());
}
// Only register the CallBackObject if EnableClientCallBacks is true
if( this.EnableClientCallBacks && !this.Page.IsClientScriptBlockRegistered(CallBackHelper.ScriptName))
{
StringBuilder script = new StringBuilder();
script.AppendFormat("<script type='text/javascript' src='{0}/{1}'></script>", this.ScriptFolderPath, CallBackHelper.ScriptName);
this.Page.RegisterClientScriptBlock(CallBackHelper.ScriptName, script.ToString());
}
}
/// <summary>
/// Helper function for getting the default Xsl arguments
/// </summary>
/// <returns></returns>
public XsltArgumentList GetXsltArguments()
{
XsltArgumentList xal = new XsltArgumentList();
xal.AddParam("jsId", string.Empty, this.JsId);
xal.AddParam("divId", string.Empty, this.DivId);
xal.AddParam("controlId", string.Empty, this.UniqueID);
xal.AddParam("enableClientCallBacks", string.Empty, this.EnableClientCallBacks);
xal.AddParam("initJs", string.Empty, initJs.ToString());
xal.AddParam("enableDragging", string.Empty, this.EnableDragging);
xal.AddParam("enableInfoWindow", string.Empty, this.EnableInfoWindow);
xal.AddParam("zoomLevel", string.Empty, this.ZoomLevel);
xal.AddParam("mapType", string.Empty, this.MapType.ToString());
xal.AddParam("friendlyControlId", string.Empty, this.FriendlyUniqueId);
return xal;
}
#endregion
#region IPostBackEventHandler Members
/// <summary>
/// Generally used for responding to server side events. We are using it
/// here to respond to client call backs. Currently supported events are
/// Click, Zoom, Move Start, Move End and Marker Click.
/// </summary>
/// <param name="eventArgument">Events will be passed from the client in {event}|{argument1},{argument2}...{argumentN} form</param>
public void RaisePostBackEvent(string eventArgument)
{
// If this isn't a call back we won't bother
if( !CallBackHelper.IsCallBack )
return;
// Always wrap callback code in a try/catch block
try
{
string[] ea = eventArgument.Split('|');
string[] args = null;
GPointEventArgs pea = null;
string evt = ea[0];
string retVal = String.Empty;
switch( evt )
{
// GMap Click event sends the coordinates of the click as event argument
case "GMap_Click":
args = ea[1].Split(',');
pea = new GPointEventArgs(float.Parse(args[0]), float.Parse(args[1]), this);
retVal = this.OnClick(pea);
break;
// GMarker Click event sends the coordinates of the click as event argument
case "GMarker_Click":
args = ea[1].Split(',');
GPoint gp = new GPoint(float.Parse(args[0]), float.Parse(args[1]));
GMarker gm = new GMarker(gp, args[2]);
pea = new GPointEventArgs(gp, gm);
retVal = this.OnMarkerClick(pea);
break;
// GMap Move Start event sends the coordinates of the center of the
// map where the move started
case "GMap_MoveStart":
args = ea[1].Split(',');
pea = new GPointEventArgs(float.Parse(args[0]), float.Parse(args[1]));
retVal = this.OnMoveStart(pea);
break;
// GMap Move End event sends the coordinates of the center of the
// map where the move ended
case "GMap_MoveEnd":
args = ea[1].Split(',');
pea = new GPointEventArgs(float.Parse(args[0]), float.Parse(args[1]));
retVal = this.OnMoveEnd(pea);
break;
// GMap Zoom event sends the old and new zoom levels
case "GMap_Zoom":
args = ea[1].Split(',');
GMapZoomEventArgs zea = new GMapZoomEventArgs(int.Parse(args[0]), int.Parse(args[1]));
retVal = this.OnZoom(zea);
break;
// GMap Client Load event
case "GMap_ClientLoad":
retVal = this.OnClientLoad();
break;
// GMap Map Type Changed event
case "GMap_MapTypeChanged":
retVal = this.OnMapTypeChanged();
break;
// Default: we don't know what the client was trying to do
default:
throw new HttpException(String.Format("Invalid GMap Event Sender: {0} Event: {1}", this.ID, evt));
}
CallBackHelper.Write(retVal);
}
// Had some odd Thread Abort Exceptions going around. Something to do with
// the default behavior of Response.End. Just ignore these exceptions
catch(Exception e)
{
if( !(e is System.Threading.ThreadAbortException) )
CallBackHelper.HandleError(e);
}
}
#endregion
#region IPostBackDataHandler Members
/// <summary>
/// Not implemented
/// </summary>
public void RaisePostDataChangedEvent() { }
/// <summary>
/// After a postback, load the Center, Span, and Bounds data passed from the client
/// </summary>
/// <param name="postDataKey"></param>
/// <param name="postCollection"></param>
/// <returns></returns>
public bool LoadPostData(string postDataKey, System.Collections.Specialized.NameValueCollection postCollection)
{
string uId = this.UniqueID;
try { _centerLatLng = (GPoint)postCollection[uId + _centerLatLngField]; } catch { }
try { _spanLatLng = (GSize)postCollection[uId + _spanLatLngField]; } catch { }
try { _boundsLatLng = (GBounds)postCollection[uId + _boundsLatLngField]; } catch { }
try { _zoomLevel = Convert.ToInt32(postCollection[uId + _zoomLevelField], Utilities.UsCulture); } catch { }
try { this.MapType = (GMapType)Utilities.StringToEnum(typeof(GMapType), postCollection[uId + _mapTypeField]); } catch { }
return false;
}
#endregion
#region Events
#region EventPlumbing
// Code required to raise events to developers /////
private static readonly object EventClick;
private static readonly object EventMarkerClick;
private static readonly object EventMoveStart;
private static readonly object EventMoveEnd;
private static readonly object EventZoom;
private static readonly object EventClientLoad;
private static readonly object EventMapTypeChanged;
static GMap()
{
GMap.EventClick = new object();
GMap.EventMarkerClick = new object();
GMap.EventMoveStart = new object();
GMap.EventMoveEnd = new object();
GMap.EventZoom = new object();
GMap.EventClientLoad = new object();
GMap.EventMapTypeChanged = new object();
}
/////
#endregion
#region Click
/// <summary>
/// Allows developers to capture the click event
/// </summary>
public event GMapClickEventHandler Click
{
add { base.Events.AddHandler(GMap.EventClick, value); }
remove { base.Events.RemoveHandler(GMap.EventClick, value); }
}
/// <summary>
/// This event is raised whenever the GMap or a GMarker is clicked.
/// The sender will be either a GMap or GMarker. The result of this event
/// is a string of javascript the developer would like evaluated client side
/// using the eval() function
/// </summary>
/// <param name="pea">The point where the click occured</param>
protected virtual string OnClick(GPointEventArgs pea)
{
GMapClickEventHandler eh = (GMapClickEventHandler)base.Events[GMap.EventClick];
if(eh != null)
{
return eh(pea.Target, pea);
}
return String.Empty;
}
#endregion
#region MarkerClick
/// <summary>
/// Allows developers to capture the click event
/// </summary>
public event GMapClickEventHandler MarkerClick
{
add { base.Events.AddHandler(GMap.EventMarkerClick, value); }
remove { base.Events.RemoveHandler(GMap.EventMarkerClick, value); }
}
/// <summary>
/// This event is raised whenever a GMarker is clicked.
/// The sender will be a new GMarker object.
/// </summary>
/// <param name="pea">The point where the click occured</param>
protected virtual string OnMarkerClick(GPointEventArgs pea)
{
GMapClickEventHandler eh = (GMapClickEventHandler)base.Events[GMap.EventMarkerClick];
if(eh != null)
{
return eh(pea.Target, pea);
}
return String.Empty;
}
#endregion
#region MoveStart
/// <summary>
/// Allows developers to capture the MoveStart event
/// </summary>
public event GMapMoveEventHandler MoveStart
{
add { base.Events.AddHandler(GMap.EventMoveStart, value); }
remove { base.Events.RemoveHandler(GMap.EventMoveStart, value); }
}
/// <summary>
/// This event is raised whenever the users starts moving the GMap. The
/// center lat/lng coordinate of the map at the start is passed as an
/// argument. The result of this event is a string of javascript the
/// developer would like evaluated client side using the eval() function
/// </summary>
/// <param name="pea">The center lat/lng coordinate of the map before the
/// move started</param>
protected virtual string OnMoveStart(GPointEventArgs pea)
{
GMapMoveEventHandler eh = (GMapMoveEventHandler)base.Events[GMap.EventMoveStart];
if(eh != null)
{
return eh(this, pea);
}
return String.Empty;
}
#endregion
#region MoveEnd
/// <summary>
/// Allows the developer to capture the MoveEnd event
/// </summary>
public event GMapMoveEventHandler MoveEnd
{
add { base.Events.AddHandler(GMap.EventMoveEnd, value); }
remove { base.Events.RemoveHandler(GMap.EventMoveEnd, value); }
}
/// <summary>
/// This event is raised whenever the users finishes moving the GMap. The
/// center lat/lng coordinate of the map at the end is passed as an
/// argument. The result of this event is a string of javascript the
/// developer would like evaluated client side using the eval() function
/// </summary>
/// <param name="pea">The center lat/lng coordinate of the map after the
/// move finished</param>
protected virtual string OnMoveEnd(GPointEventArgs pea)
{
GMapMoveEventHandler eh = (GMapMoveEventHandler)base.Events[GMap.EventMoveEnd];
if(eh != null)
{
return eh(this, pea);
}
return String.Empty;
}
#endregion
#region Zoom
/// <summary>
/// Allows the developer to capture the Zoom event
/// </summary>
public event GMapZoomEventHandler Zoom
{
add { base.Events.AddHandler(GMap.EventZoom, value); }
remove { base.Events.RemoveHandler(GMap.EventZoom, value); }
}
/// <summary>
/// This event is raised whenever the user zooms the map. The result of
/// this event is a string of javascript the developer would like evaluated
/// client side using the eval() function
/// </summary>
/// <param name="zea">The old and new zoom levels</param>
protected virtual string OnZoom(GMapZoomEventArgs zea)
{
GMapZoomEventHandler eh = (GMapZoomEventHandler)base.Events[GMap.EventZoom];
if(eh != null)
{
return eh(this, zea);
}
return String.Empty;
}
#endregion
#region ClientLoad
/// <summary>
/// Allows the developer to take action after the Map has loaded on the client
/// </summary>
public event GMapEventHandler ClientLoad
{
add { base.Events.AddHandler(GMap.EventClientLoad, value); }
remove { base.Events.RemoveHandler(GMap.EventClientLoad, value); }
}
/// <summary>
/// This event is raised after the map has loaded on the client. This gives the developer
/// access to the Center, Bounds, and Span values and take the necessary action
/// </summary>
protected virtual string OnClientLoad()
{
GMapEventHandler eh = (GMapEventHandler)base.Events[GMap.EventClientLoad];
if(eh != null)
{
return eh(this, null);
}
return String.Empty;
}
#endregion
#region MapTypeChanged
/// <summary>
/// Allows the developer to take action whenver the Map Type has changed
/// </summary>
public event GMapEventHandler MapTypeChanged
{
add { base.Events.AddHandler(GMap.EventMapTypeChanged, value); }
remove { base.Events.RemoveHandler(GMap.EventMapTypeChanged, value); }
}
/// <summary>
/// This event is raised whenever the user changes the Map Type
/// </summary>
protected virtual string OnMapTypeChanged()
{
GMapEventHandler eh = (GMapEventHandler)base.Events[GMap.EventMapTypeChanged];
if(eh != null)
{
return eh(this, null);
}
return String.Empty;
}
#endregion
#endregion
}
#region Delegates
/// <summary>
/// Handler for the GMap_Click event
/// </summary>
public delegate string GMapClickEventHandler(object s, GPointEventArgs pea);
/// <summary>
/// Handler for the GMap_Move events
/// </summary>
public delegate string GMapMoveEventHandler (object s, GPointEventArgs pea);
/// <summary>
/// Handler for the GMap_Zoom event
/// </summary>
public delegate string GMapZoomEventHandler (object s, GMapZoomEventArgs zea);
/// <summary>
/// Handler for the GMap_ClientLoad and GMap_MapTypeChanged events
/// </summary>
public delegate string GMapEventHandler (object s, EventArgs e);
#endregion
}