From ffeefac861fd6ec1f190cf1ecaaebe88e00ecd5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Franz=20H=C3=A4ring?= Date: Tue, 22 Dec 2020 14:28:50 +0100 Subject: [PATCH 1/6] Extensions to the listable entities: - New PolylineList class. - New method SetMultipleAsync() to combine remove, add and modify operations on lists. - New static methods ManageAsync() to manage lifetime of lists. --- .../Maps/Extension/CircleList.cs | 38 ++++ .../Maps/Extension/ListableEntityListBase.cs | 32 +++ .../Maps/Extension/MarkerList.cs | 39 ++++ .../Maps/Extension/PolylineList.cs | 205 ++++++++++++++++++ GoogleMapsComponents/Maps/Polyline.cs | 62 +----- GoogleMapsComponents/Maps/PolylineOptions.cs | 2 +- 6 files changed, 324 insertions(+), 54 deletions(-) create mode 100644 GoogleMapsComponents/Maps/Extension/PolylineList.cs diff --git a/GoogleMapsComponents/Maps/Extension/CircleList.cs b/GoogleMapsComponents/Maps/Extension/CircleList.cs index 85e66cc2..d1ec7379 100644 --- a/GoogleMapsComponents/Maps/Extension/CircleList.cs +++ b/GoogleMapsComponents/Maps/Extension/CircleList.cs @@ -47,11 +47,49 @@ public static async Task CreateAsync(IJSRuntime jsRuntime, Dictionar return obj; } + /// + /// Manage list over lifetime: Create and remove list depending on entity count; + /// entities will be removed, added or changed to mirror the given set. + /// + /// + /// The list to manage. May be null. + /// + /// + /// + /// + /// The managed list. Assign to the variable you used as parameter. + /// + public static async Task ManageAsync(CircleList list,IJSRuntime jsRuntime, Dictionary opts) + { + if (opts.Count==0) { + if (list!=null) { + await list.SetMultipleAsync(opts); + list=null; + } + } else { + if (list==null) { + list = await CircleList.CreateAsync(jsRuntime,opts); + } else { + await list.SetMultipleAsync(opts); + } + } + return list; + } + private CircleList(JsObjectRef jsObjectRef, Dictionary circles) : base(jsObjectRef, circles) { } + /// + /// Set the set of entities; entities will be removed, added or changed to mirror the given set. + /// + /// + /// + public async Task SetMultipleAsync(Dictionary opts) + { + await base.SetMultipleAsync(opts, "google.maps.Circle"); + } /// /// Only keys not matching with existent Circle keys will be created diff --git a/GoogleMapsComponents/Maps/Extension/ListableEntityListBase.cs b/GoogleMapsComponents/Maps/Extension/ListableEntityListBase.cs index 17f2af4c..477ac0e2 100644 --- a/GoogleMapsComponents/Maps/Extension/ListableEntityListBase.cs +++ b/GoogleMapsComponents/Maps/Extension/ListableEntityListBase.cs @@ -30,6 +30,38 @@ public void Dispose() } } + /// + /// Set the set of entities; entities will be removed, added or changed to mirror the given set. + /// + /// + /// + public async Task SetMultipleAsync(Dictionary opts, string googleMapListableEntityTypeName) + { + var nonVisibles = new Dictionary(); + var lToRemove = new List(); + var dictToAdd = new Dictionary(); + var dictToChange = new Dictionary(); + foreach (var sKey in this.BaseListableEntities.Keys) { + if (!opts.ContainsKey(sKey)) { + lToRemove.Add(sKey); + } + } + foreach (var sKey in lToRemove) { + nonVisibles[sKey]=false; + } + foreach (var sKey in opts.Keys) { + if (this.BaseListableEntities.ContainsKey(sKey)) { + dictToChange[sKey]=opts[sKey]; + } else { + dictToAdd[sKey]=opts[sKey]; + } + } + await this.SetVisibles(nonVisibles); + await this.RemoveMultipleAsync(lToRemove); + await this.AddMultipleAsync(dictToAdd,googleMapListableEntityTypeName); + await this.SetOptions(dictToChange); + } + /// /// only keys not matching with existent listable entity keys will be created /// diff --git a/GoogleMapsComponents/Maps/Extension/MarkerList.cs b/GoogleMapsComponents/Maps/Extension/MarkerList.cs index d1ccc64f..31d38b1e 100644 --- a/GoogleMapsComponents/Maps/Extension/MarkerList.cs +++ b/GoogleMapsComponents/Maps/Extension/MarkerList.cs @@ -48,11 +48,50 @@ public static async Task CreateAsync(IJSRuntime jsRuntime, Dictionar return obj; } + /// + /// Manage list over lifetime: Create and remove list depending on entity count; + /// entities will be removed, added or changed to mirror the given set. + /// + /// + /// The list to manage. May be null. + /// + /// + /// + /// + /// The managed list. Assign to the variable you used as parameter. + /// + public static async Task ManageAsync(MarkerList list,IJSRuntime jsRuntime, Dictionary opts) + { + if (opts.Count==0) { + if (list!=null) { + await list.SetMultipleAsync(opts); + list=null; + } + } else { + if (list==null) { + list = await MarkerList.CreateAsync(jsRuntime,opts); + } else { + await list.SetMultipleAsync(opts); + } + } + return list; + } + private MarkerList(JsObjectRef jsObjectRef, Dictionary markers) : base(jsObjectRef, markers) { } + /// + /// Set the set of entities; entities will be removed, added or changed to mirror the given set. + /// + /// + /// + public async Task SetMultipleAsync(Dictionary opts) + { + await base.SetMultipleAsync(opts, "google.maps.Marker"); + } + /// /// only keys not matching with existent Marker keys will be created /// diff --git a/GoogleMapsComponents/Maps/Extension/PolylineList.cs b/GoogleMapsComponents/Maps/Extension/PolylineList.cs new file mode 100644 index 00000000..92cf5327 --- /dev/null +++ b/GoogleMapsComponents/Maps/Extension/PolylineList.cs @@ -0,0 +1,205 @@ +using Microsoft.JSInterop; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using GoogleMapsComponents.Maps; + +namespace GoogleMapsComponents.Maps.Extension +{ + /// + /// A class able to manage a lot of Polyline objects and get / set their + /// properties at the same time, eventually with different values + /// Main concept is that each Polyline to can be distinguished by other ones need + /// to have a "unique key" with a "external world mean", so not necessary it's GUID + /// + /// All properties should be called With a Dictionary indicating for each Polyline(related to that key) the corresponding related property value + /// + public class PolylineList : ListableEntityListBase + { + public Dictionary Polylines => base.BaseListableEntities; + + /// + /// Create circles list + /// + /// + /// Dictionary of desired Polyline keys and PolylineOptions values. Key as any type unique key. Not nessary Guid + /// new instance of PolylineList class will be returned with its Polylines dictionary member populated with the corresponding results + public static async Task CreateAsync(IJSRuntime jsRuntime, Dictionary opts) + { + JsObjectRef jsObjectRef = new JsObjectRef(jsRuntime, Guid.NewGuid()); + + PolylineList obj; + if (opts.Count > 0) + { + Dictionary jsObjectRefs = await JsObjectRef.CreateMultipleAsync( + jsRuntime, + "google.maps.Polyline", + opts.ToDictionary(e => e.Key, e => (object)e.Value)); + Dictionary objs = jsObjectRefs.ToDictionary(e => e.Key, e => new Polyline(e.Value)); + obj = new PolylineList(jsObjectRef, objs); + } + else + { + obj = new PolylineList(jsObjectRef, null); + } + + return obj; + } + + /// + /// Manage list over lifetime: Create and remove list depending on entity count; + /// entities will be removed, added or changed to mirror the given set. + /// + /// + /// The list to manage. May be null. + /// + /// + /// + /// + /// The managed list. Assign to the variable you used as parameter. + /// + public static async Task ManageAsync(PolylineList list,IJSRuntime jsRuntime, Dictionary opts) + { + if (opts.Count==0) { + if (list!=null) { + await list.SetMultipleAsync(opts); + list=null; + } + } else { + if (list==null) { + list = await PolylineList.CreateAsync(jsRuntime,opts); + } else { + await list.SetMultipleAsync(opts); + } + } + return list; + } + + private PolylineList(JsObjectRef jsObjectRef, Dictionary polylines) + : base(jsObjectRef, polylines) + { + } + + /// + /// Set the set of entities; entities will be removed, added or changed to mirror the given set. + /// + /// + /// + public async Task SetMultipleAsync(Dictionary opts) + { + await base.SetMultipleAsync(opts, "google.maps.Polyline"); + } + + /// + /// Only keys not matching with existent Polyline keys will be created + /// + /// + /// + public async Task AddMultipleAsync(Dictionary opts) + { + await base.AddMultipleAsync(opts, "google.maps.Polyline"); + } + + public Task> GetBounds(List filterKeys = null) + { + List matchingKeys = ComputeMathingKeys(filterKeys); + + if (matchingKeys.Any()) + { + Dictionary internalMapping = ComputeInternalMapping(matchingKeys); + Dictionary dictArgs = ComputeDictArgs(matchingKeys); + + return _jsObjectRef.InvokeMultipleAsync( + "getBounds", + dictArgs).ContinueWith(e => e.Result.ToDictionary(r => internalMapping[new Guid(r.Key)], r => r.Value)); + } + else + { + return ComputeEmptyResult(); + } + } + + public Task> GetCenters(List filterKeys = null) + { + List matchingKeys = ComputeMathingKeys(filterKeys); + + if (matchingKeys.Any()) + { + Dictionary internalMapping = ComputeInternalMapping(matchingKeys); + Dictionary dictArgs = ComputeDictArgs(matchingKeys); + + return _jsObjectRef.InvokeMultipleAsync( + "getCenter", + dictArgs).ContinueWith(e => e.Result.ToDictionary(r => internalMapping[new Guid(r.Key)], r => r.Value)); + } + else + { + return ComputeEmptyResult(); + } + } + + public Task> GetEditables(List filterKeys = null) + { + List matchingKeys = ComputeMathingKeys(filterKeys); + + if (matchingKeys.Any()) + { + Dictionary internalMapping = ComputeInternalMapping(matchingKeys); + Dictionary dictArgs = ComputeDictArgs(matchingKeys); + + return _jsObjectRef.InvokeMultipleAsync( + "getEditable", + dictArgs).ContinueWith(e => e.Result.ToDictionary(r => internalMapping[new Guid(r.Key)], r => r.Value)); + } + else + { + return ComputeEmptyResult(); + } + } + + public Task> GetRadiuses(List filterKeys = null) + { + List matchingKeys = ComputeMathingKeys(filterKeys); + + if (matchingKeys.Any()) + { + Dictionary internalMapping = ComputeInternalMapping(matchingKeys); + Dictionary dictArgs = ComputeDictArgs(matchingKeys); + + return _jsObjectRef.InvokeMultipleAsync( + "getRadius", + dictArgs).ContinueWith(e => e.Result.ToDictionary(r => internalMapping[new Guid(r.Key)], r => r.Value)); + } + else + { + return ComputeEmptyResult(); + } + } + + public Task SetCenters(Dictionary centers) + { + Dictionary dictArgs = centers.ToDictionary(e => Polylines[e.Key].Guid, e => (object)e.Value); + return _jsObjectRef.InvokeMultipleAsync( + "setCenter", + dictArgs); + } + + public Task SetEditables(Dictionary editables) + { + Dictionary dictArgs = editables.ToDictionary(e => Polylines[e.Key].Guid, e => (object)e.Value); + return _jsObjectRef.InvokeMultipleAsync( + "setEditable", + dictArgs); + } + + public Task SetRadiuses(Dictionary radiuses) + { + Dictionary dictArgs = radiuses.ToDictionary(e => Polylines[e.Key].Guid, e => (object)e.Value); + return _jsObjectRef.InvokeMultipleAsync( + "setRadius", + dictArgs); + } + } +} diff --git a/GoogleMapsComponents/Maps/Polyline.cs b/GoogleMapsComponents/Maps/Polyline.cs index f66826e0..491366c4 100644 --- a/GoogleMapsComponents/Maps/Polyline.cs +++ b/GoogleMapsComponents/Maps/Polyline.cs @@ -9,15 +9,8 @@ namespace GoogleMapsComponents.Maps /// /// A polyline is a linear overlay of connected line segments on the map. /// - public class Polyline : IDisposable + public class Polyline : ListableEntityBase, IDisposable { - private readonly JsObjectRef _jsObjectRef; - private Map _map; - - /// - /// Access polyline using guid and window._blazorGoogleMapsObjects[GUID_STRING] - /// - public Guid Guid => _jsObjectRef.Guid; /// /// Create a polyline using the passed PolylineOptions, which specify both the path of the polyline and the stroke style to use when drawing the polyline. @@ -32,17 +25,19 @@ public async static Task CreateAsync(IJSRuntime jsRuntime, PolylineOpt } /// - /// Create a polyline using the passed PolylineOptions, which specify both the path of the polyline and the stroke style to use when drawing the polyline. + /// Constructor for use in ListableEntityListBase. Must be the first constructor! /// - private Polyline(JsObjectRef jsObjectRef, PolylineOptions opts = null) + internal Polyline(JsObjectRef jsObjectRef) + :base(jsObjectRef) { - _jsObjectRef = jsObjectRef; - _map = opts?.Map; } - public void Dispose() + /// + /// Create a polyline using the passed PolylineOptions, which specify both the path of the polyline and the stroke style to use when drawing the polyline. + /// + private Polyline(JsObjectRef jsObjectRef, PolylineOptions opts) + :this(jsObjectRef) { - _jsObjectRef.Dispose(); } /// @@ -65,15 +60,6 @@ public Task GetEditable() "getEditable"); } - /// - /// Returns the map on which this shape is attached. - /// - /// - public Map GetMap() - { - return _map; - } - /// /// Retrieves the path. /// @@ -118,21 +104,6 @@ public Task SetEditable(bool editable) editable); } - /// - /// Renders this shape on the specified map. - /// If map is set to null, the shape will be removed. - /// - /// - /// - public Task SetMap(Map map) - { - _map = map; - - return _jsObjectRef.InvokeAsync( - "setMap", - map); - } - public Task SetOptions(PolylineOptions options) { return _jsObjectRef.InvokeAsync( @@ -164,20 +135,5 @@ public Task SetVisible(bool visible) visible); } - public async Task AddListener(string eventName, Action handler) - { - var listenerRef = await _jsObjectRef.InvokeWithReturnedObjectRefAsync( - "addListener", eventName, handler); - - return new MapEventListener(listenerRef); - } - - public async Task AddListener(string eventName, Action handler) - { - var listenerRef = await _jsObjectRef.InvokeWithReturnedObjectRefAsync( - "addListener", eventName, handler); - - return new MapEventListener(listenerRef); - } } } diff --git a/GoogleMapsComponents/Maps/PolylineOptions.cs b/GoogleMapsComponents/Maps/PolylineOptions.cs index fc3b9337..1d545d98 100644 --- a/GoogleMapsComponents/Maps/PolylineOptions.cs +++ b/GoogleMapsComponents/Maps/PolylineOptions.cs @@ -9,7 +9,7 @@ namespace GoogleMapsComponents.Maps /// /// PolylineOptions object used to define the properties that can be set on a Polyline. /// - public class PolylineOptions + public class PolylineOptions : ListableEntityOptionsBase { /// /// Indicates whether this Polyline handles mouse events. From a582644693dbfd4b6f429d0f001facb62036f20b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Franz=20H=C3=A4ring?= Date: Tue, 22 Dec 2020 19:06:15 +0100 Subject: [PATCH 2/6] Click event handler added to all ListableEntityBase derived classes. The click event provides MouseEvent data plus entity key and value. --- .../Maps/Extension/CircleList.cs | 19 +++++++-------- .../Maps/Extension/ListableEntityListBase.cs | 23 ++++++++++++++++++- .../Maps/Extension/MarkerList.cs | 19 +++++++-------- .../Maps/Extension/PolylineList.cs | 19 +++++++-------- 4 files changed, 46 insertions(+), 34 deletions(-) diff --git a/GoogleMapsComponents/Maps/Extension/CircleList.cs b/GoogleMapsComponents/Maps/Extension/CircleList.cs index d1ec7379..a9d0d5ca 100644 --- a/GoogleMapsComponents/Maps/Extension/CircleList.cs +++ b/GoogleMapsComponents/Maps/Extension/CircleList.cs @@ -30,19 +30,12 @@ public static async Task CreateAsync(IJSRuntime jsRuntime, Dictionar JsObjectRef jsObjectRef = new JsObjectRef(jsRuntime, Guid.NewGuid()); CircleList obj; - if (opts.Count > 0) - { Dictionary jsObjectRefs = await JsObjectRef.CreateMultipleAsync( jsRuntime, "google.maps.Circle", opts.ToDictionary(e => e.Key, e => (object)e.Value)); Dictionary objs = jsObjectRefs.ToDictionary(e => e.Key, e => new Circle(e.Value)); obj = new CircleList(jsObjectRef, objs); - } - else - { - obj = new CircleList(jsObjectRef, null); - } return obj; } @@ -59,7 +52,7 @@ public static async Task CreateAsync(IJSRuntime jsRuntime, Dictionar /// /// The managed list. Assign to the variable you used as parameter. /// - public static async Task ManageAsync(CircleList list,IJSRuntime jsRuntime, Dictionary opts) + public static async Task ManageAsync(CircleList list,IJSRuntime jsRuntime, Dictionary opts,Action clickCallback=null) { if (opts.Count==0) { if (list!=null) { @@ -68,10 +61,14 @@ public static async Task ManageAsync(CircleList list,IJSRuntime jsRu } } else { if (list==null) { - list = await CircleList.CreateAsync(jsRuntime,opts); - } else { - await list.SetMultipleAsync(opts); + list = await CircleList.CreateAsync(jsRuntime,new Dictionary()); + if (clickCallback!=null) { + list.EntityClicked+=(sender,e)=>{ + clickCallback(e.MouseEvent,e.Key,e.Entity); + }; + } } + await list.SetMultipleAsync(opts); } return list; } diff --git a/GoogleMapsComponents/Maps/Extension/ListableEntityListBase.cs b/GoogleMapsComponents/Maps/Extension/ListableEntityListBase.cs index 477ac0e2..9f745ab7 100644 --- a/GoogleMapsComponents/Maps/Extension/ListableEntityListBase.cs +++ b/GoogleMapsComponents/Maps/Extension/ListableEntityListBase.cs @@ -62,6 +62,21 @@ public async Task SetMultipleAsync(Dictionary opts, await this.SetOptions(dictToChange); } + public class EntityMouseEvent + { + public MouseEvent MouseEvent { get; set; } + public string Key { get; set; } + public TEntityBase Entity { get; set; } + } + + public event EventHandler EntityClicked; + + private void FireEvent(EventHandler eventHandler, TEvent ea) { + if (eventHandler!=null) { + eventHandler(this,ea); + } + } + /// /// only keys not matching with existent listable entity keys will be created /// @@ -93,7 +108,13 @@ public virtual async Task AddMultipleAsync(Dictionary("click",(e) => { + this.FireEvent(this.EntityClicked,new EntityMouseEvent { MouseEvent=e,Key=key,Entity=entity }); + }); + } } } } diff --git a/GoogleMapsComponents/Maps/Extension/MarkerList.cs b/GoogleMapsComponents/Maps/Extension/MarkerList.cs index 31d38b1e..d7b10525 100644 --- a/GoogleMapsComponents/Maps/Extension/MarkerList.cs +++ b/GoogleMapsComponents/Maps/Extension/MarkerList.cs @@ -31,19 +31,12 @@ public static async Task CreateAsync(IJSRuntime jsRuntime, Dictionar JsObjectRef jsObjectRef = new JsObjectRef(jsRuntime, Guid.NewGuid()); MarkerList obj; - if (opts.Count > 0) - { Dictionary jsObjectRefs = await JsObjectRef.CreateMultipleAsync( jsRuntime, "google.maps.Marker", opts.ToDictionary(e => e.Key, e => (object)e.Value)); Dictionary objs = jsObjectRefs.ToDictionary(e => e.Key, e => new Marker(e.Value)); obj = new MarkerList(jsObjectRef, objs); - } - else - { - obj = new MarkerList(jsObjectRef, null); - } return obj; } @@ -60,7 +53,7 @@ public static async Task CreateAsync(IJSRuntime jsRuntime, Dictionar /// /// The managed list. Assign to the variable you used as parameter. /// - public static async Task ManageAsync(MarkerList list,IJSRuntime jsRuntime, Dictionary opts) + public static async Task ManageAsync(MarkerList list,IJSRuntime jsRuntime, Dictionary opts,Action clickCallback=null) { if (opts.Count==0) { if (list!=null) { @@ -69,10 +62,14 @@ public static async Task ManageAsync(MarkerList list,IJSRuntime jsRu } } else { if (list==null) { - list = await MarkerList.CreateAsync(jsRuntime,opts); - } else { - await list.SetMultipleAsync(opts); + list = await MarkerList.CreateAsync(jsRuntime,new Dictionary()); + if (clickCallback!=null) { + list.EntityClicked+=(sender,e)=>{ + clickCallback(e.MouseEvent,e.Key,e.Entity); + }; + } } + await list.SetMultipleAsync(opts); } return list; } diff --git a/GoogleMapsComponents/Maps/Extension/PolylineList.cs b/GoogleMapsComponents/Maps/Extension/PolylineList.cs index 92cf5327..1f0be92f 100644 --- a/GoogleMapsComponents/Maps/Extension/PolylineList.cs +++ b/GoogleMapsComponents/Maps/Extension/PolylineList.cs @@ -31,19 +31,12 @@ public static async Task CreateAsync(IJSRuntime jsRuntime, Diction JsObjectRef jsObjectRef = new JsObjectRef(jsRuntime, Guid.NewGuid()); PolylineList obj; - if (opts.Count > 0) - { Dictionary jsObjectRefs = await JsObjectRef.CreateMultipleAsync( jsRuntime, "google.maps.Polyline", opts.ToDictionary(e => e.Key, e => (object)e.Value)); Dictionary objs = jsObjectRefs.ToDictionary(e => e.Key, e => new Polyline(e.Value)); obj = new PolylineList(jsObjectRef, objs); - } - else - { - obj = new PolylineList(jsObjectRef, null); - } return obj; } @@ -60,7 +53,7 @@ public static async Task CreateAsync(IJSRuntime jsRuntime, Diction /// /// The managed list. Assign to the variable you used as parameter. /// - public static async Task ManageAsync(PolylineList list,IJSRuntime jsRuntime, Dictionary opts) + public static async Task ManageAsync(PolylineList list,IJSRuntime jsRuntime, Dictionary opts,Action clickCallback=null) { if (opts.Count==0) { if (list!=null) { @@ -69,10 +62,14 @@ public static async Task ManageAsync(PolylineList list,IJSRuntime } } else { if (list==null) { - list = await PolylineList.CreateAsync(jsRuntime,opts); - } else { - await list.SetMultipleAsync(opts); + list = await PolylineList.CreateAsync(jsRuntime,new Dictionary()); + if (clickCallback!=null) { + list.EntityClicked+=(sender,e)=>{ + clickCallback(e.MouseEvent,e.Key,e.Entity); + }; + } } + await list.SetMultipleAsync(opts); } return list; } From 943b331b906ae6ec0f7f8c6ca0f5d94aa53f61b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Franz=20H=C3=A4ring?= Date: Wed, 23 Dec 2020 08:08:34 +0100 Subject: [PATCH 3/6] Added doc comment to ListableEntityListBase.EntityClicked event. --- .../Maps/Extension/ListableEntityListBase.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/GoogleMapsComponents/Maps/Extension/ListableEntityListBase.cs b/GoogleMapsComponents/Maps/Extension/ListableEntityListBase.cs index 9f745ab7..98204bce 100644 --- a/GoogleMapsComponents/Maps/Extension/ListableEntityListBase.cs +++ b/GoogleMapsComponents/Maps/Extension/ListableEntityListBase.cs @@ -69,6 +69,13 @@ public class EntityMouseEvent public TEntityBase Entity { get; set; } } + /// + /// Entity clicked event containing coordinates, entity key and value. + /// This event will be fired for entities which are being added after at least one + /// event handler is added to this event. + /// Adding handlers to the event will slow down adding entities by a small amount. + /// If no handler is added, performance is not impaired. + /// public event EventHandler EntityClicked; private void FireEvent(EventHandler eventHandler, TEvent ea) { From c77fb20933b47b164df51c88b02fabd220134174 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Franz=20H=C3=A4ring?= Date: Thu, 24 Dec 2020 09:45:47 +0100 Subject: [PATCH 4/6] New method ListableEntityListBase.AddListeners to add event listeners in one JS call to multiple objects of the same type. The callback contains the event arguments plus the entity reference key. So far there is no method to remove the event listeners. --- GoogleMapsComponents/Helper.cs | 27 +++++++++++++++---- GoogleMapsComponents/JsObjectRef.cs | 9 +++++++ .../Maps/Extension/ListableEntityListBase.cs | 15 ++++++++--- GoogleMapsComponents/wwwroot/objectManager.js | 21 +++++++++++++++ 4 files changed, 64 insertions(+), 8 deletions(-) diff --git a/GoogleMapsComponents/Helper.cs b/GoogleMapsComponents/Helper.cs index a95b65d9..c82842ee 100644 --- a/GoogleMapsComponents/Helper.cs +++ b/GoogleMapsComponents/Helper.cs @@ -39,11 +39,7 @@ private static string SerializeObject(object obj) return value; } - internal static async Task MyInvokeAsync( - this IJSRuntime jsRuntime, - string identifier, - params object[] args) - { + private static IEnumerable MakeArgJsFriendly( IJSRuntime jsRuntime,IEnumerable args) { var jsFriendlyArgs = args .Select(arg => { @@ -100,6 +96,16 @@ internal static async Task MyInvokeAsync( } } }); + return jsFriendlyArgs; + } + + internal static async Task MyInvokeAsync( + this IJSRuntime jsRuntime, + string identifier, + params object[] args) + { + + var jsFriendlyArgs = MakeArgJsFriendly(jsRuntime,args); if (typeof(IJsObjectRef).IsAssignableFrom(typeof(TRes))) { @@ -145,6 +151,17 @@ internal static async Task MyInvokeAsync( } } + internal static async Task MyAddListenerAsync( + this IJSRuntime jsRuntime, + string identifier, + params object[] args) + { + + var jsFriendlyArgs = MakeArgJsFriendly(jsRuntime,args); + + return await jsRuntime.InvokeAsync(identifier, jsFriendlyArgs); + } + private static async Task InvokeAsync( this IJSRuntime jsRuntime, string identifier, diff --git a/GoogleMapsComponents/JsObjectRef.cs b/GoogleMapsComponents/JsObjectRef.cs index 08198c31..e35cc3d4 100644 --- a/GoogleMapsComponents/JsObjectRef.cs +++ b/GoogleMapsComponents/JsObjectRef.cs @@ -185,6 +185,15 @@ public Task InvokeMultipleAsync(string functionName, Dictionary di ); } + public Task AddMultipleListenersAsync(string eventName, Dictionary dictArgs) + { + return _jsRuntime.MyAddListenerAsync( + "googleMapsObjectManager.addMultipleListeners", + new object[] { dictArgs.Select(e => e.Key.ToString()).ToList(), eventName } + .Concat(dictArgs.Values).ToArray() + ); + } + public Task InvokeAsync(string functionName, params object[] args) { return _jsRuntime.MyInvokeAsync( diff --git a/GoogleMapsComponents/Maps/Extension/ListableEntityListBase.cs b/GoogleMapsComponents/Maps/Extension/ListableEntityListBase.cs index 98204bce..575b0d9d 100644 --- a/GoogleMapsComponents/Maps/Extension/ListableEntityListBase.cs +++ b/GoogleMapsComponents/Maps/Extension/ListableEntityListBase.cs @@ -117,14 +117,15 @@ public virtual async Task AddMultipleAsync(Dictionary("click",(e) => { - this.FireEvent(this.EntityClicked,new EntityMouseEvent { MouseEvent=e,Key=key,Entity=entity }); + await this.AddListeners(objs.Keys,"click",(mev,key) => { + this.FireEvent(this.EntityClicked,new EntityMouseEvent { MouseEvent=mev,Key=key,Entity=BaseListableEntities[key] }); }); } } } - } /// /// only Marker having keys matching with existent keys will be removed @@ -306,5 +307,13 @@ public virtual Task SetVisibles(Dictionary visibles) "setVisible", dictArgs); } + + public virtual async Task AddListeners(IEnumerable enitityKeys, string eventName, Action handler) + { + Dictionary dictArgs = enitityKeys.ToDictionary(key => BaseListableEntities[key].Guid, key => (object)new Action((e)=>{ + handler(e,key); + })); + await _jsObjectRef.AddMultipleListenersAsync(eventName,dictArgs); + } } } \ No newline at end of file diff --git a/GoogleMapsComponents/wwwroot/objectManager.js b/GoogleMapsComponents/wwwroot/objectManager.js index ba2d648a..23b98fbd 100644 --- a/GoogleMapsComponents/wwwroot/objectManager.js +++ b/GoogleMapsComponents/wwwroot/objectManager.js @@ -409,6 +409,27 @@ window.googleMapsObjectManager = { return results; }, + //add event listeners to multiple objects of the same type + addMultipleListeners: async function (args) { + let args2 = args.slice(2).map(arg => tryParseJson(arg)); + + var results = {}; + let objs = []; + let guids = JSON.parse(args[0]); + + for (var i = 0; i < guids.length; i++) { + objs[i] = window._blazorGoogleMapsObjects[guids[i]]; + let args3 = []; + args3 = args3.concat(guids[i]).concat("addListener").concat(args[1]).concat(args2[i]); + + let result = googleMapsObjectManager.invoke(args3); + } + + //console.log(results); + + return !0; + }, + readObjectPropertyValue: function (args) { let obj = window._blazorGoogleMapsObjects[args[0]]; From 733ab94318cac89df97b3e88a15e6f9563dbe18c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Franz=20H=C3=A4ring?= Date: Thu, 24 Dec 2020 13:00:50 +0100 Subject: [PATCH 5/6] Added a demo page for fast creation and event listening of multiple circles. Management of CircleList is done with the new CircleList.ManageAsync method. --- ServerSideDemo/Pages/MapCircleListPage.razor | 16 ++ .../Pages/MapCircleListPage.razor.cs | 74 ++++++++++ ServerSideDemo/Shared/NavMenu.razor | 139 +++++++++--------- 3 files changed, 162 insertions(+), 67 deletions(-) create mode 100644 ServerSideDemo/Pages/MapCircleListPage.razor create mode 100644 ServerSideDemo/Pages/MapCircleListPage.razor.cs diff --git a/ServerSideDemo/Pages/MapCircleListPage.razor b/ServerSideDemo/Pages/MapCircleListPage.razor new file mode 100644 index 00000000..6d56f21f --- /dev/null +++ b/ServerSideDemo/Pages/MapCircleListPage.razor @@ -0,0 +1,16 @@ +@page "/map-circle-list" +@using System.Diagnostics +@using Microsoft.AspNetCore.Components +@using Microsoft.AspNetCore.Components.Forms +@using GoogleMapsComponents +@using GoogleMapsComponents.Maps +@using GoogleMapsComponents.Maps.Coordinates + +

Google Map Circle List

+ + + + + Bunch size:
+
+
\ No newline at end of file diff --git a/ServerSideDemo/Pages/MapCircleListPage.razor.cs b/ServerSideDemo/Pages/MapCircleListPage.razor.cs new file mode 100644 index 00000000..a50b6f10 --- /dev/null +++ b/ServerSideDemo/Pages/MapCircleListPage.razor.cs @@ -0,0 +1,74 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using GoogleMapsComponents; +using GoogleMapsComponents.Maps; +using GoogleMapsComponents.Maps.Coordinates; +using GoogleMapsComponents.Maps.Extension; +using Microsoft.AspNetCore.Components; +using Microsoft.JSInterop; +using ServerSideDemo.Shared; + +namespace ServerSideDemo.Pages +{ + public partial class MapCircleListPage : ComponentBase + { + private GoogleMap map1; + private MapOptions mapOptions; + int bunchsize = 10; + + private CircleList circleList=null; + private Dictionary circleOptionsByRef = new Dictionary(); + private int lastId=0; + + protected override void OnInitialized() { + mapOptions = new MapOptions() { + Zoom = 13, + Center = new LatLngLiteral() { + Lat = 48.994249, + Lng = 12.190451 + }, + MapTypeId = MapTypeId.Roadmap + }; + } + + /// + /// Create a bunch of circles, put them into a dictionary with reference ids and display them on the map. + /// + private async void CreateBunchOfCircles() { + int howMany = this.bunchsize; + var bounds = await this.map1.InteropObject.GetBounds(); + double maxRadius = (bounds.North-bounds.South)*111111.0/(10+Math.Sqrt(howMany)); + var colors = new string[] { "#FFFFFF","#9132D1","#FFD800","#846A00","#AAC643","#C96A00","#B200FF","#CD6A00","#00A321","#7F6420", + }; + var rnd = new Random(); + for (int i=0;i{ + // Circle has been clicked --> delete it. + this.circleOptionsByRef.Remove(sKey); + await this.RefreshCircleList(); + }); + } + + } +} \ No newline at end of file diff --git a/ServerSideDemo/Shared/NavMenu.razor b/ServerSideDemo/Shared/NavMenu.razor index 95e44135..11617f43 100644 --- a/ServerSideDemo/Shared/NavMenu.razor +++ b/ServerSideDemo/Shared/NavMenu.razor @@ -6,74 +6,79 @@
-
@code { From 4a725c68fc67561c96e148343bd54856d0e4461c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Franz=20H=C3=A4ring?= Date: Thu, 24 Dec 2020 13:07:23 +0100 Subject: [PATCH 6/6] Indentation restored to original value. --- ServerSideDemo/Shared/NavMenu.razor | 145 ++++++++++++++-------------- 1 file changed, 72 insertions(+), 73 deletions(-) diff --git a/ServerSideDemo/Shared/NavMenu.razor b/ServerSideDemo/Shared/NavMenu.razor index 11617f43..5d34241d 100644 --- a/ServerSideDemo/Shared/NavMenu.razor +++ b/ServerSideDemo/Shared/NavMenu.razor @@ -6,79 +6,78 @@
- +
@code {