private static ImageSource CreateImage(TileSource tileSource, Tile tile)
        {
            ImageSource image = null;

            try
            {
                image = BitmapFrame.Create(tileSource.GetUri(tile.XIndex, tile.Y, tile.ZoomLevel));
            }
            catch (Exception ex)
            {
                Trace.TraceWarning("Creating tile image failed: {0}", ex.Message);
            }

            return(image);
        }
        private void LoadPendingTiles(Dispatcher dispatcher, TileSource tileSource, string sourceName, bool animateOpacity)
        {
            var  setImageAction  = new Action <Tile, ImageSource>((t, i) => t.SetImageSource(i, animateOpacity));
            var  imageTileSource = tileSource as ImageTileSource;
            Tile tile;

            while (pendingTiles.TryDequeue(out tile))
            {
                byte[]      buffer = null;
                ImageSource image  = null;

                if (imageTileSource != null)
                {
                    image = LoadImage(imageTileSource, tile);
                }
                else
                {
                    var uri = tileSource.GetUri(tile.XIndex, tile.Y, tile.ZoomLevel);

                    if (uri != null)
                    {
                        if (uri.Scheme == "file") // create from FileStream as creating from URI leaves the file open
                        {
                            image = CreateImage(uri.AbsolutePath);
                        }
                        else
                        {
                            buffer = DownloadImage(uri);
                            image  = CreateImage(buffer);
                        }
                    }
                }

                if (image != null || !tile.HasImageSource) // do not set null if tile already has an image (from cache)
                {
                    dispatcher.BeginInvoke(setImageAction, DispatcherPriority.Render, tile, image);
                }

                if (buffer != null && image != null)
                {
                    Cache.Set(GetCacheKey(sourceName, tile), buffer, new CacheItemPolicy {
                        SlidingExpiration = CacheExpiration
                    });
                }
            }

            Interlocked.Decrement(ref threadCount);
        }
        private void GetTiles(List <Tile> tiles, Dispatcher dispatcher, TileSource tileSource,
                              string sourceName, int maxDownloads, bool animateOpacity)
        {
            var imageTileSource = tileSource as ImageTileSource;

            if (imageTileSource != null)
            {
                if (!imageTileSource.CanLoadAsync) // call LoadImage in UI thread
                {
                    var setImageAction = new Action <Tile, ImageTileSource>((t, ts) => t.SetImageSource(LoadImage(ts, t), animateOpacity));

                    foreach (var tile in tiles)
                    {
                        dispatcher.BeginInvoke(setImageAction, DispatcherPriority.Render, tile, imageTileSource);
                    }

                    return;
                }
            }
            else if (!tileSource.UriFormat.StartsWith("file:")) // load local image files asynchronously, without caching
            {
                if (Cache == null || string.IsNullOrWhiteSpace(sourceName))
                {
                    // no caching here: use default asynchronous downloading and caching done by WPF

                    var setImageAction = new Action <Tile, TileSource>((t, ts) => t.SetImageSource(CreateImage(ts, t), animateOpacity));

                    foreach (var tile in tiles)
                    {
                        dispatcher.BeginInvoke(setImageAction, DispatcherPriority.Render, tile, tileSource);
                    }

                    return;
                }
                else
                {
                    var setImageAction = new Action <Tile, ImageSource>((t, i) => t.SetImageSource(i, animateOpacity));
                    var outdatedTiles  = new List <Tile>(tiles.Count);

                    foreach (var tile in tiles)
                    {
                        var key    = GetCacheKey(sourceName, tile);
                        var buffer = Cache.Get(key) as byte[];
                        var image  = CreateImage(buffer);

                        if (image != null)
                        {
                            var creationTime = BitConverter.ToInt64(buffer, 0);

                            if (DateTime.FromBinary(creationTime) + CacheUpdateAge < DateTime.UtcNow)
                            {
                                dispatcher.Invoke(setImageAction, DispatcherPriority.Render, tile, image); // synchronously before enqueuing
                                outdatedTiles.Add(tile);                                                   // update outdated cache
                            }
                            else
                            {
                                dispatcher.BeginInvoke(setImageAction, DispatcherPriority.Render, tile, image);
                            }
                        }
                        else
                        {
                            pendingTiles.Enqueue(tile); // not yet cached
                        }
                    }

                    tiles = outdatedTiles; // enqueue outdated tiles at last
                }
            }

            foreach (var tile in tiles)
            {
                pendingTiles.Enqueue(tile);
            }

            while (threadCount < Math.Min(pendingTiles.Count, maxDownloads))
            {
                Interlocked.Increment(ref threadCount);

                ThreadPool.QueueUserWorkItem(o => LoadPendingTiles(dispatcher, tileSource, sourceName, animateOpacity));
            }
        }