Ticket T195734
Visible to All Users
Duplicate

We have closed this ticket because another page addresses its subject:

Web.UI - Provide the capability to display business objects on a map in ASP.NET applications

How to use Google Maps in XAF ASP.NET application

Answers approved by DevExpress Support

created 10 years ago (modified 10 years ago)

Add a new ListEditor class and register JavaScript code in accordance with the Google Maps documentation to show a list of markers on a map (for more details, see https://developers.google.com/maps/documentation/javascript/examples/marker-simple):

C#
[ListEditor(typeof(IGoogleMapsMarker), true)] public class GoogleMapsListEditor : ListEditor { public GoogleMapsListEditor(IModelListView model) : base(model) { } protected override void AssignDataSourceToControl(object dataSource) { WebWindow.CurrentRequestWindow.RegisterClientScript(Model.Id + "_markers_script", ConvertToJS(Enumerable.Cast<IGoogleMapsMarker>((IEnumerable)dataSource)), true); } protected override object CreateControlsCore() { return CreateGoogleMapControl(); } public static string ConvertToJS(IGoogleMapsMarker marker) { List<IGoogleMapsMarker> markers = new List<IGoogleMapsMarker>(); if(marker != null) { markers.Add(marker); } return ConvertToJS(markers); } public static string ConvertToJS(IEnumerable<IGoogleMapsMarker> markers) { string markersScript = "var markers = ["; if(markers != null) { foreach(IGoogleMapsMarker marker in markers) { string title = (marker.Title == null) ? "" : marker.Title.Replace("'", "\\'"); markersScript += string.Format(@"{{ title: '{0}', latitude: {1}, longitude: {2} }},", title, marker.Position.Latitude, marker.Position.Longitude); } } markersScript = markersScript.TrimEnd(','); markersScript += "];"; return markersScript; } public static WebControl CreateGoogleMapControl() { Panel ctrl = new Panel(); ctrl.ID = "map-canvas"; ctrl.Height = new Unit(500); ctrl.Width = new Unit(500); ASPxPanel geoMapControlPanel = new ASPxPanel(); geoMapControlPanel.ID = "MapPanel"; geoMapControlPanel.Height = new Unit(600); geoMapControlPanel.Width = new Unit(600); geoMapControlPanel.Controls.Add(ctrl); geoMapControlPanel.Load += delegate(object sender, EventArgs e) { geoMapControlPanel.ClientSideEvents.Init = @" function ShowMap() { var mapOptions = { center: new google.maps.LatLng(" + GoogleMapsDefaults.MapCenterLatitude + ", " + GoogleMapsDefaults.MapCenterLongitude + @"), zoom: 4, }; var map = new google.maps.Map(document.getElementById('" + ctrl.ClientID + @"'), mapOptions); if (typeof markers === 'undefined') { } else { for (i = 0; i < markers.length; i++) { var data = markers[i] var latlng = new google.maps.LatLng(data.latitude, data.longitude); var marker = new google.maps.Marker({ position: latlng, map: map, title: data.title }); } } }"; }; return geoMapControlPanel; }

Note that Google Maps JavaScript code is not designed to be registered with the help of the WebWindow.RegisterClientScriptInclude method. Instead, update the 'Default.aspx' file in your XAF ASP.NET project and include a reference to Google Maps JavaScript code as shown below:

ASPx
<head runat="server"> <title>Main Page</title> <meta http-equiv="Expires" content="0" /> <script type="text/javascript" src="http://maps.googleapis.com/maps/api/js?sensor=false" ></script> </head>

Add a new WebPropertyEditor class to show a single marker in a similar way:

C#
[PropertyEditor(typeof(IGoogleMapsMarker), true)] public class GoogleMapsPropertyEditor : WebPropertyEditor { public GoogleMapsPropertyEditor(Type objectType, IModelMemberViewItem info) : base(objectType, info) { AllowEdit.SetItemValue(GetType().Name, false); //readonly } protected override WebControl CreateViewModeControlCore() { return GoogleMapsListEditor.CreateGoogleMapControl(); } protected override void ReadViewModeValueCore() { WebWindow.CurrentRequestWindow.RegisterClientScript(Model.Id + "_markers_script", GoogleMapsListEditor.ConvertToJS((IGoogleMapsMarker)PropertyValue), true); }

This solution demonstrates a basic scenario and we will be happy to review screenshots or a short video that demonstrates how the Maps control helps your clients do their work.
This information will help us investigate whether it is possible to provide a universal and useful solution for this scenario.
Don't hesitate to write to us here or in a separate private ticket if you are interested in this integration. Your time and cooperation are appreciated.

UPDATED by Dennis(DevExpress Support):
An improved version of the sample is available at GoogleMapsInXafWebUIExample archive attached to this post. Main improvements are described and shown in the Google Maps integration in XAF Web UI - Looking for real use-cases! blog post.

    Show previous comments (28)
    Dennis Garavsky (DevExpress) 10 years ago

      @INFO CL: I could not replicate any problems with version 14.2 and Chrome/Internet Explorer. I suggest you check out the browser's console window for JavaScript errors.

        Hello Dennis,

        I used the following custom list editor,

        C#
        using BeYat.BusinessObjectsLibrary.Helpers; using DevExpress.Data; using DevExpress.ExpressApp.Editors; using DevExpress.ExpressApp.Model; using DevExpress.ExpressApp.Web; using DevExpress.Web; using DevExpress.Xpo; using System; using System.Collections; using System.Collections.Generic; using System.ComponentModel; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Web.UI.WebControls; namespace BeYat.Module.Web.Editors { [ListEditor(typeof(IGoogleMapsMarker), true)] public class ASPxGoogleMapsListEditor : ListEditor { public ASPxGoogleMapsListEditor(IModelListView model) : base(model) { } protected override void AssignDataSourceToControl(object dataSource) { //IListSource listSource = (IListSource)dataSource; //IListServer listServer = (IListServer)listSource.GetList(); //IList rows = listServer.GetAllFilteredAndSortedRows(); WebWindow.CurrentRequestWindow.RegisterClientScript(Model.Id + "_markers_script", ConvertToJS(Enumerable.Cast<IGoogleMapsMarker>((IEnumerable)dataSource)), true); } protected override object CreateControlsCore() { return CreateGoogleMapControl(); } public static string ConvertToJS(IGoogleMapsMarker marker) { List<IGoogleMapsMarker> markers = new List<IGoogleMapsMarker>(); if(marker != null) { markers.Add(marker); } return ConvertToJS(markers); } public static string ConvertToJS(IEnumerable<IGoogleMapsMarker> markers) { string markersScript = "var markers = ["; if(markers != null) { foreach(IGoogleMapsMarker marker in markers) { string title = (marker.Title == null) ? "" : marker.Title.Replace("'", "\\'"); //markersScript += string.Format(@"{{ title: '{0}', latitude: {1}, longitude: {2} }},", title, marker.Position.Latitude, marker.Position.Longitude); markersScript += string.Format(@"{{ title: '{0}', latitude: {1}, longitude: {2} }},", title, marker.Position.Latitude.ToString().Replace(",", "."), marker.Position.Longitude.ToString().Replace(",", ".")); } } markersScript = markersScript.TrimEnd(','); markersScript += "];"; return markersScript; } public static WebControl CreateGoogleMapControl() { Panel ctrl = new Panel(); ctrl.ID = "map-canvas"; ctrl.Height = Unit.Percentage(100); ctrl.Width = Unit.Percentage(100); ASPxPanel geoMapControlPanel = new ASPxPanel(); geoMapControlPanel.ID = "MapPanel"; geoMapControlPanel.Height = new Unit(600); geoMapControlPanel.Width = Unit.Percentage(100); geoMapControlPanel.Controls.Add(ctrl); geoMapControlPanel.Load += delegate(object sender, EventArgs e) { geoMapControlPanel.ClientSideEvents.Init = @" function ShowMap() { //create empty LatLngBounds object var bounds = new google.maps.LatLngBounds(); var infowindow = new google.maps.InfoWindow(); var mapOptions = { center: new google.maps.LatLng(" + GoogleMapsDefaults.MapCenterLatitude.ToString().Replace(",", ".") + ", " + GoogleMapsDefaults.MapCenterLongitude.ToString().Replace(",", ".") + @"), zoom: 6, }; var map = new google.maps.Map(document.getElementById('" + ctrl.ClientID + @"'), mapOptions); if (typeof markers === 'undefined') { } else { for (i = 0; i < markers.length; i++) { var data = markers[i] var latlng = new google.maps.LatLng(data.latitude, data.longitude); var marker = new google.maps.Marker({ position: latlng, map: map, title: data.title }); //extend the bounds to include each marker's position bounds.extend(marker.position); google.maps.event.addListener(marker, 'click', (function(marker, i) { return function() { infowindow.setContent(locations[i][0]); infowindow.open(map, marker); } })(marker, i)); } //now fit the map to the newly inclusive bounds map.fitBounds(bounds); } }"; }; return geoMapControlPanel; } public override DevExpress.ExpressApp.Templates.IContextMenuTemplate ContextMenuTemplate { get { return null; } } public override IList GetSelectedObjects() { return new object[0]; } public override void Refresh() {} public override DevExpress.ExpressApp.SelectionType SelectionType { get { return DevExpress.ExpressApp.SelectionType.None; } } } }

        and now I tried to add a couple of Filters on the ListView but when I switch from one to the other the Map doesn't refresh. But If I press refresh it works as expected.
        Can you tell me how I can implement the Refresh method, because I tried the following and it doesn't work.

        Thank you

        C#
        private ProxyCollection collection; private WebControl control; public override void Refresh() { if (collection != null) { collection.Refresh(); } if (control != null) { // nor WebControl nor AspxPanel have a refresh method } }

        DevExpress Support Team 9 years ago

          @Anthony:

          We created a separate ticket on your behalf (T419111: How to implement the Refresh method for a custom list editor that shows Google Maps in XAF ASP.NET application). It has been placed in our processing queue and will be answered shortly.

          Disclaimer: The information provided on DevExpress.com and affiliated web properties (including the DevExpress Support Center) is provided "as is" without warranty of any kind. Developer Express Inc disclaims all warranties, either express or implied, including the warranties of merchantability and fitness for a particular purpose. Please refer to the DevExpress.com Website Terms of Use for more information in this regard.

          Confidential Information: Developer Express Inc does not wish to receive, will not act to procure, nor will it solicit, confidential or proprietary materials and information from you through the DevExpress Support Center or its web properties. Any and all materials or information divulged during chats, email communications, online discussions, Support Center tickets, or made available to Developer Express Inc in any manner will be deemed NOT to be confidential by Developer Express Inc. Please refer to the DevExpress.com Website Terms of Use for more information in this regard.