ThinkGeo.com    |     Documentation    |     Premium Support

Detect if WpfMap is busy / Gracefully exit

    Hi,


 Under a certain menuitem on my app I do a me.close in order to close the window and end the application. The problem is that if the user clicks on it when the wpfmap is refreshing an overlay, i start having problems such as null reference exceptions and locks.


 Is there any way of detecting if wpfmap is actually busy? Is there any recommended way to gracefully close the app, considering that there is an overlay that refresh every second? (and I don't know how to figure out if it finished refreshing)



How are you closing the app when user clicks on this button?  Are you calling Environment.Exit() in button's click or command handler?    This should terminate your process.  If you call Environment.Exit(), then you  will also want to do this in App.xaml.cs


 Application.Current.ShutdownMode = ShutdownMode.OnExplicitShutdown;



 


           




I was just calling me.close() 
  
  But is there any way to know if WPFmap is busy anyway?

 Carlos,  one option for you is to replace your map with an instance of this class and hook into its DrawingProgressChanged event.  Since map uses main dispatcher, it will be hard to say when it is busy as other parts of your UI are also sharing same dispatcher.  BTW, is it not about 9 PM out there? 



  public class ExtendedWpfMap : WpfMap
    {       
        /// <summary>
        /// Occurs when drawing progress changed.
        /// </summary>
        public event EventHandler<DrawingProgressChangedExtendedWpfMapEventArgs> DrawingProgressChanged;

        public ExtendedWpfMap()
            : base()
        {
            Overlays.CollectionChanged += Overlays_CollectionChanged;
            this.ComputeCurrentProgressUsingBuffer = true;
        }

        /// <summary>
        /// Set this to true if you want draw progress to
        /// include progress of buffer tiles.  Default is true.
        /// Setting this property to false requires an expensive 
        /// Intersects operation to find out which tiles are within 
        /// visible map extent.  While this works, it is not recommended.
        /// </summary>
        public bool ComputeCurrentProgressUsingBuffer { get; set; }        

        protected virtual void OnDrawingProgressChanged(DrawingProgressChangedExtendedWpfMapEventArgs e)
        {
            if (DrawingProgressChanged != null)
            {
                DrawingProgressChanged(this, e);
            }
        }

        private void Overlays_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            switch (e.Action)
            {
                case NotifyCollectionChangedAction.Add:
                    int newCount = e.NewItems.Count;
                    TileOverlay tileOverlay = null;
                    foreach (Overlay overlay in e.NewItems)
                    {
                        tileOverlay = overlay as TileOverlay;
                        if (tileOverlay != null && tileOverlay.TileType != TileType.SingleTile)
                            tileOverlay.DrawTilesProgressChanged += tileOverlay_DrawTilesProgressChanged;
                        else
                        {
                            if (newCount == 1 && tileOverlay != null)
                            {
                                tileOverlay.Drawn += OnSingleTileDrawn;                                                                
                            }
                        }                        
                    }
                    break;
                case NotifyCollectionChangedAction.Remove:
                    newCount = e.OldItems.Count;
                    foreach (Overlay overlay in e.OldItems)
                    {
                        tileOverlay = overlay as TileOverlay;
                        if (tileOverlay != null && tileOverlay.TileType != TileType.SingleTile)                        
                            tileOverlay.DrawTilesProgressChanged -= tileOverlay_DrawTilesProgressChanged;                                                                                                                       
                        
                        tileOverlay.Drawn -= OnSingleTileDrawn;                                                                                    
                        var layerO = overlay as LayerOverlay;
                        if (layerO != null)
                        {
                            foreach (var layer in layerO.Layers)
                            {
                                layer.IsVisible = false;
                                layer.Close();
                            }
                            
                            layerO.Layers.Clear();
                        }

                        overlay.Dispose();
                    }
                    break;
                default:
                    break;
            }
        }

        void OnSingleTileDrawn(object sender, DrawnOverlayEventArgs e)
        {
            OnDrawingProgressChanged(new DrawingProgressChangedExtendedWpfMapEventArgs(100, null));
        }   

        int previousPercentage = 0;
        private void tileOverlay_DrawTilesProgressChanged(object sender, DrawTilesProgressChangedTileOverlayEventArgs e)
        {
            TileOverlay currentTileOverlay = (TileOverlay)sender;
            currentTileOverlay.OverlayCanvas.Tag = e.ProgressPercentage;

            IEnumerable<TileOverlay> tileOverlays = Overlays.OfType<TileOverlay>()
                .Where((to => to.TileType != TileType.SingleTile))
                .Where(to => to.OverlayCanvas.Tag != null);

            if (ComputeCurrentProgressUsingBuffer)
                ComputeCurrentDrawProgressUsingBuffer(tileOverlays);
            else
                ComputeCurrenDrawProgressWithoutBuffer(tileOverlays);
        }

        private void ComputeCurrenDrawProgressWithoutBuffer(IEnumerable<TileOverlay> tileOverlays)
        {
            var tilesInVisibleExtent = tileOverlays.SelectMany(o => ((Canvas)o.OverlayCanvas.Children[0]).Children
                                                           .OfType<ThinkGeo.MapSuite.WpfDesktopEdition.Tile>()
                                                           .Where(tile => o.MapArguments.CurrentExtent.Intersects(tile.TargetExtent)));

            var totalTiles = tileOverlays.SelectMany(o => ((Canvas)o.OverlayCanvas.Children[0]).Children
                                                           .OfType<ThinkGeo.MapSuite.WpfDesktopEdition.Tile>());
            int total = tilesInVisibleExtent.Count();
            int currentProgress = 100;

            if (total != 0)
            {
                var tilesLoaded = tilesInVisibleExtent.Where(tile => tile.IsOpened);
                currentProgress = tilesLoaded.Count() * 100 / total;
            }

            if (Math.Abs(currentProgress - previousPercentage) >= 5 || currentProgress == 100 && previousPercentage != 100)
            {
                previousPercentage = currentProgress;               
                OnDrawingProgressChanged(new DrawingProgressChangedExtendedWpfMapEventArgs(currentProgress, null));
            }
        }        

        private void ComputeCurrentDrawProgressUsingBuffer(IEnumerable<TileOverlay> tileOverlays)
        {
            int total = tileOverlays.Count();
            int currentProgress = 100;
            if (total != 0)
            {
                int progress = 0;
                foreach (TileOverlay tileOverlay in tileOverlays)
                {
                    if (tileOverlay.OverlayCanvas.Tag != null)
                    {
                        var overlayProgress = (int)tileOverlay.OverlayCanvas.Tag;
                        progress += overlayProgress;
                    }
                }

                currentProgress = progress / total;

                if (Math.Abs(currentProgress - previousPercentage) >= 5 || currentProgress == 100 && previousPercentage != 100)
                {
                    previousPercentage = currentProgress;                    
                    OnDrawingProgressChanged(new DrawingProgressChangedExtendedWpfMapEventArgs(currentProgress, null));
                }
            }
        }

        #region Map extensions
        public bool ContainsLayer(string layerName)
        {
            return this.Overlays.FirstOrDefault(o => string.CompareOrdinal(o.Name, layerName) == 0) != null;
        }

        public bool SafelyRefresh(Overlay overlay)
        {
            bool successfullyRefreshed = false;

            try
            {
                this.Refresh(overlay);
                successfullyRefreshed = true;
            }
            catch (Exception ex)
            {
                ExceptionPolicy.HandleException(ex, ExceptionHandlingPolicyNames.LogPolicy);                
            }

            return successfullyRefreshed;
        }

        public bool SafelyRefresh()
        {
            bool successfullyRefreshed = false;

            try
            {
                this.Refresh();
                successfullyRefreshed = true;
            }
            catch (Exception ex)
            {
                ExceptionPolicy.HandleException(ex, ExceptionHandlingPolicyNames.LogPolicy);                
            }

            return successfullyRefreshed;
        }

        public bool SafelyRefresh(IEnumerable<Overlay> overlays)
        {
            bool successfullyRefreshed = false;

            try
            {
                this.Refresh(overlays);
                successfullyRefreshed = true;
            }
            catch (Exception ex)
            {
                ExceptionPolicy.HandleException(ex, ExceptionHandlingPolicyNames.LogPolicy);                
            }

            return successfullyRefreshed;
        }        
        
        public IEnumerable<FeatureLayer> GetFeatureLayers()
        {
            return GetFeatureLayers(null);
        }

        public IEnumerable<FeatureLayer> GetFeatureLayers( IEnumerable<string> overlaysToExclude)
        {
            var featureLayers = new Collection<FeatureLayer>();
            IEnumerable<LayerOverlay> layerOverlays = null;
            layerOverlays = this.Overlays.Where(o => !o.IsEmpty && o.IsVisible).OfType<LayerOverlay>();                 
            if (overlaysToExclude != null)
                layerOverlays = layerOverlays.Where(lo => lo != null && !overlaysToExclude.Contains(lo.Name));
            else
                layerOverlays = layerOverlays.Where(lo => lo != null);

            var _featureLayers = layerOverlays.SelectMany(l => l.Layers).OfType<FeatureLayer>();            
            featureLayers.AddRange(_featureLayers);
            featureLayers.Add(this.EditOverlay.EditShapesLayer);

            return featureLayers;
        }

        #endregion
    }


Klaus, thanks for your warm help.


Hi Carlos,
Sorry for the delayed response. I’ve read your post and tried to recreate the error according to your description, but it seems that the application will be shut down correctly even if there’re Layers being drawn on the WpfMap control. Could you please send us the sample that encounters this error?
You can handle the OverlayDrawing and OverlayDrawn events defined in the WfpMap Control, there you can get some general information about Overlay drawing and drawn, and you can even go deep into the instance overlays whose references are passed by the event argument to get detailed information.
Another suggestion is that you can call Overlay.Dispose() to release the resources.
Further questions please let me know.
Thanks.
James

Hi Klaus, 



Thanks a lot!!! It's exactly what I needed. I remember you were fighting with the Drawing tiles percentage time ago, and I now see it was a complex issue ;) 



Yes, it was arround 9 PM... if you have kids you probably figured out they are time consuming, and when they go bed at 9 it's like a relaxing quiet time. When I got my daughter on bed to sleep I use to have some time when I can check email, forums etc. You know how tight deadlines are...


 James,


I tried with OverlayDrawing and OverlayDrawn, they are fired but the map continues drawiing after OverlayDrawn is fired. I want to show a progress bar with the Tiles drawing percentage, so I'll incorporate Klaus' code and use this in order to avoid the problem.


 I'll come back with the results.


Carlos.




 



Thanks for you guys information. 
  
 Let me know if you have more comments. 
  
 James

Carlos, glad to know posted code helped. I thought it would and have made recommendations for ThinkGeo to add this to their core.  It makes a lot of sense to also get an overall drawing percentage from the map, in addition to progress just  for one tile.  BTW, that was another Howard magic that I just modified a little to make it more usable.    
  
 I understand your 'late night shifts".  Did this a lot earlier in this project as I have 3 little ones running around the house and when the go to bed at 8-9PM I log on to catch ThinkGeo late night support on questions I cannot wait till morning . 


Klaus, 
  
 Thanks for your idea that add kind of event and get an overall drawing percentage. 
  
 We have already had a similar event DrawingProgressChanged in MapSuiteCore, however it only works for Layer not Overlay. 
  
 Thanks, 
 James

Hi Klaus, 
  
  Your solution works like a charm. Thanks a lot!! 
  
 Carlos. 
  
 James: Isn’t it a good idea to include this in the MapSuite core code?

Carlos, 
  
 The idea is working to Overlay not Layer, however MapSuite Core only has Layer doesn’t has Overlay, if we implement this idea to Overlay, we have to update WpfDesktopEdition. Either way, I have added it to our working tracking system and consider together to decide if we will include or not. 
  
 Thanks, 
 James