private void GetTiles(IEnumerable <Tile> tiles, Dispatcher dispatcher, TileSource tileSource, string sourceName, int maxDownloads) { var useCache = Cache != null && !string.IsNullOrEmpty(sourceName) && !(tileSource is ImageTileSource) && !tileSource.UriFormat.StartsWith("file:"); foreach (var tile in tiles) { ImageSource cachedImage = null; if (useCache && GetCachedImage(CacheKey(sourceName, tile), out cachedImage)) { dispatcher.BeginInvoke(new Action <Tile, ImageSource>((t, i) => t.SetImage(i)), tile, cachedImage); } else { pendingTiles.Enqueue(new PendingTile(tile, cachedImage)); } } var newTaskCount = Math.Min(pendingTiles.Count, maxDownloads) - taskCount; while (newTaskCount-- > 0) { Interlocked.Increment(ref taskCount); Task.Run(() => LoadPendingTiles(dispatcher, tileSource, sourceName)); } }
/// <summary> /// Loads all pending tiles from the tiles collection. /// If tileSource.UriFormat starts with "http" and sourceName is a non-empty string, /// tile images will be cached in the TileImageLoader's Cache (if that is not null). /// </summary> public void LoadTiles(IEnumerable <Tile> tiles, TileSource tileSource, string sourceName) { tileQueue.Clear(); tiles = tiles.Where(tile => tile.Pending); if (tiles.Any() && tileSource != null) { if (Cache != null && tileSource.UriFormat != null && tileSource.UriFormat.StartsWith("http") && !string.IsNullOrEmpty(sourceName)) { loadTile = tile => LoadCachedTileAsync(tile, tileSource, sourceName); } else { loadTile = tile => LoadTileAsync(tile, tileSource); } tileQueue.Enqueue(tiles); while (taskCount < Math.Min(tileQueue.Count, MaxLoadTasks)) { Interlocked.Increment(ref taskCount); Task.Run(() => LoadTilesFromQueueAsync()); } } }
/// <summary> /// Loads all pending tiles from the tiles collection in up to MaxLoadTasks parallel Tasks. /// If the UriFormat of the TileSource starts with "http" and the sourceName string is non-empty, /// tile images are cached in the TileImageLoader's Cache. /// </summary> public void LoadTilesAsync(IEnumerable <Tile> tiles, TileSource tileSource, string sourceName) { tileQueue.Clear(); if (tileSource != null) { tileQueue.Enqueue(tiles); var newTasks = Math.Min(tileQueue.Count, MaxLoadTasks) - taskCount; if (newTasks > 0) { Func <Tile, Task> loadTileFunc; if (Cache != null && tileSource.UriFormat != null && tileSource.UriFormat.StartsWith("http") && !string.IsNullOrEmpty(sourceName)) { loadTileFunc = tile => LoadCachedTileImageAsync(tile, tileSource, sourceName); } else { loadTileFunc = tile => LoadTileImageAsync(tile, tileSource); } Interlocked.Add(ref taskCount, newTasks); while (--newTasks >= 0) { Task.Run(() => LoadTilesFromQueueAsync(loadTileFunc)); } } } }
private async Task LoadPendingTilesAsync(TileSource tileSource, string sourceName) { Tile tile; while (pendingTiles.TryPop(out tile)) { tile.Pending = false; try { var uri = tileSource.GetUri(tile.XIndex, tile.Y, tile.ZoomLevel); if (uri != null) { var extension = Path.GetExtension(uri.LocalPath); if (string.IsNullOrEmpty(extension) || extension == ".jpeg") { extension = ".jpg"; } var cacheKey = string.Format(CacheKeyFormat, sourceName, tile.ZoomLevel, tile.XIndex, tile.Y, extension); await LoadTileImageAsync(tile, uri, cacheKey); } } catch (Exception ex) { Debug.WriteLine("TileImageLoader: {0}/{1}/{2}: {3}", tile.ZoomLevel, tile.XIndex, tile.Y, ex.Message); } } }
private async Task LoadTileImageAsync(Tile tile, TileSource tileSource, string sourceName) { if (tileSource != null) { if (Cache != null && tileSource.UriFormat != null && tileSource.UriFormat.StartsWith("http") && !string.IsNullOrEmpty(sourceName)) { var uri = tileSource.GetUri(tile.XIndex, tile.Y, tile.ZoomLevel); if (uri != null) { var extension = Path.GetExtension(uri.LocalPath); if (string.IsNullOrEmpty(extension) || extension == ".jpeg") { extension = ".jpg"; } var cacheKey = string.Format(CacheKeyFormat, sourceName, tile.ZoomLevel, tile.XIndex, tile.Y, extension); await LoadCachedTileImageAsync(tile, uri, cacheKey).ConfigureAwait(false); } } else { await LoadTileImageAsync(tile, tileSource).ConfigureAwait(false); } } }
/// <summary> /// Loads all pending tiles from the tiles collection. /// If tileSource.UriFormat starts with "http" and sourceName is a non-empty string, /// tile images will be cached in the TileImageLoader's Cache (if it's not null). /// The method is async void because it implements void ITileImageLoader.LoadTiles /// and is not awaited when it is called in MapTileLayer.UpdateTiles(). /// </summary> public async void LoadTiles(IEnumerable <Tile> tiles, TileSource tileSource, string sourceName) { tileQueue.Clear(); tiles = tiles.Where(tile => tile.Pending); if (tiles.Any() && tileSource != null) { if (Cache != null && tileSource.UriFormat != null && tileSource.UriFormat.StartsWith("http") && !string.IsNullOrEmpty(sourceName)) { loadTileImage = tile => LoadCachedTileImageAsync(tile, tileSource, sourceName); } else { loadTileImage = tile => LoadTileImageAsync(tile, tileSource); } tileQueue.Enqueue(tiles); var numTasks = Math.Min(tileQueue.Count, MaxLoadTasks); var tasks = Enumerable.Range(0, numTasks).Select(n => LoadTilesFromQueueAsync()); await Task.WhenAll(tasks).ConfigureAwait(false); } }
private static async Task LoadTileImageAsync(Tile tile, TileSource tileSource) { var image = await tileSource.LoadImageAsync(tile.XIndex, tile.Y, tile.ZoomLevel).ConfigureAwait(false); if (image != null) { SetTileImageAsync(tile, image); } }
private async Task LoadPendingTiles(TileSource tileSource, string sourceName) { PendingTile pendingTile; var cache = Cache; if (cache == null || sourceName == null) { while (pendingTiles.TryDequeue(out pendingTile)) { await DownloadImage(pendingTile.Tile, pendingTile.Image, pendingTile.Uri, null); } } else { while (pendingTiles.TryDequeue(out pendingTile)) { var tile = pendingTile.Tile; var image = pendingTile.Image; var uri = pendingTile.Uri; var extension = Path.GetExtension(uri.LocalPath); if (string.IsNullOrEmpty(extension) || extension == ".jpeg") { extension = ".jpg"; } var cacheKey = string.Format(@"{0}\{1}\{2}\{3}{4}", sourceName, tile.ZoomLevel, tile.XIndex, tile.Y, extension); var cacheItem = await cache.GetAsync(cacheKey); var loaded = false; if (cacheItem == null || cacheItem.Expiration <= DateTime.UtcNow) { loaded = await DownloadImage(tile, image, uri, cacheKey); } if (!loaded && cacheItem != null && cacheItem.Buffer != null) { using (var stream = new InMemoryRandomAccessStream()) { await stream.WriteAsync(cacheItem.Buffer); await stream.FlushAsync(); stream.Seek(0); await LoadImageFromStream(tile, image, stream); } } } } Interlocked.Decrement(ref taskCount); }
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) { var imageTileSource = tileSource as ImageTileSource; PendingTile pendingTile; while (pendingTiles.TryDequeue(out pendingTile)) { var tile = pendingTile.Tile; 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.IsAbsoluteUri) { image = LoadImage(uri.OriginalString); } else if (uri.Scheme == "file") { image = LoadImage(uri.LocalPath); } else { image = DownloadImage(uri, CacheKey(sourceName, tile)) ?? pendingTile.CachedImage; // use possibly cached image if download failed } } } if (image != null) { dispatcher.BeginInvoke(new Action <Tile, ImageSource>((t, i) => t.SetImage(i)), tile, image); } else { tile.SetImage(null); } } Interlocked.Decrement(ref taskCount); }
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 because creating from URI leaves the file open { image = CreateImage(uri.LocalPath); } else { buffer = DownloadImage(uri); image = CreateImage(buffer); } } } if (image != null || !tile.HasImageSource) // set null image if tile does not yet have an ImageSource { dispatcher.BeginInvoke(setImageAction, tile, image); } if (image != null && buffer != null && Cache != null && !string.IsNullOrWhiteSpace(sourceName)) { Cache.Set(TileCache.Key(sourceName, tile), buffer, new CacheItemPolicy { SlidingExpiration = CacheExpiration }); } } Interlocked.Decrement(ref threadCount); }
private static async Task LoadCachedTileAsync(Tile tile, TileSource tileSource, string cacheName) { var uri = tileSource.GetUri(tile.XIndex, tile.Y, tile.ZoomLevel); if (uri != null) { var extension = Path.GetExtension(uri.LocalPath); if (string.IsNullOrEmpty(extension) || extension == ".jpeg") { extension = ".jpg"; } var cacheKey = string.Format("{0}/{1}/{2}/{3}{4}", cacheName, tile.ZoomLevel, tile.XIndex, tile.Y, extension); await LoadCachedTileAsync(tile, uri, cacheKey).ConfigureAwait(false); } }
private async Task LoadTileImageAsync(TileSource tileSource, Tile tile) { tile.Pending = false; try { var imageSource = await tileSource.LoadImageAsync(tile.XIndex, tile.Y, tile.ZoomLevel); if (imageSource != null) { tile.SetImage(imageSource); } } catch (Exception ex) { Debug.WriteLine("TileImageLoader: {0}/{1}/{2}: {3}", tile.ZoomLevel, tile.XIndex, tile.Y, ex.Message); } }
private async Task LoadCachedTileImageAsync(Tile tile, TileSource tileSource, string sourceName) { var uri = tileSource.GetUri(tile.XIndex, tile.Y, tile.ZoomLevel); if (uri != null) { var extension = Path.GetExtension(uri.LocalPath); if (string.IsNullOrEmpty(extension) || extension == ".jpeg") { extension = ".jpg"; } var cacheKey = string.Format(CacheKeyFormat, sourceName, tile.ZoomLevel, tile.XIndex, tile.Y, extension); await LoadCachedTileImageAsync(tile, uri, cacheKey); } }
private void GetTiles(List <Tile> tiles, Dispatcher dispatcher, TileSource tileSource, string sourceName, bool animateOpacity, int maxDownloads) { if (Cache != null && !string.IsNullOrWhiteSpace(sourceName) && !(tileSource is ImageTileSource) && !tileSource.UriFormat.StartsWith("file:")) { 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 buffer = Cache.Get(TileCache.Key(sourceName, tile)) as byte[]; var image = CreateImage(buffer); if (image == null) { pendingTiles.Enqueue(tile); // not yet cached } else if (CacheUpdateAge > TimeSpan.Zero && TileCache.CreationTime(buffer) + CacheUpdateAge < DateTime.UtcNow) { dispatcher.Invoke(setImageAction, tile, image); // synchronously before enqueuing outdatedTiles.Add(tile); // update outdated cache } else { dispatcher.BeginInvoke(setImageAction, tile, image); } } tiles = outdatedTiles; // enqueue outdated tiles after current tiles } 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)); } }
private async Task LoadTileImageAsync(Tile tile, TileSource tileSource) { var tcs = new TaskCompletionSource <object>(); await tile.Image.Dispatcher.RunAsync(CoreDispatcherPriority.Low, async() => { try { tile.SetImage(await tileSource.LoadImageAsync(tile.XIndex, tile.Y, tile.ZoomLevel)); tcs.SetResult(null); } catch (Exception ex) { tcs.SetException(ex); } }); await tcs.Task; }
private async Task LoadPendingTiles(TileQueue tileQueue, TileSource tileSource, string cacheName) { while (tileQueue.TryDequeue(out var tile)) { try { await LoadTile(tile, tileSource, cacheName).ConfigureAwait(false); } catch (Exception ex) { Debug.WriteLine($"TileImageLoader: {tile.ZoomLevel}/{tile.XIndex}/{tile.Y}: {ex.Message}"); } if (Progress != null && !tileQueue.IsCanceled) { Interlocked.Increment(ref progressLoaded); Progress.Report((double)progressLoaded / progressTotal); } } }
/// <summary> /// Loads all pending tiles from the tiles collection. /// If tileSource.UriFormat starts with "http" and cacheName is a non-empty string, /// tile images will be cached in the TileImageLoader's Cache - if that is not null. /// </summary> public Task LoadTiles(IEnumerable <Tile> tiles, TileSource tileSource, string cacheName) { pendingTiles?.Cancel(); TileSource = tileSource; if (tileSource != null) { pendingTiles = new TileQueue(tiles); var numTasks = Math.Min(pendingTiles.Count, MaxLoadTasks); if (numTasks > 0) { if (Progress != null) { progressTotal = pendingTiles.Count; progressLoaded = 0; Progress.Report(0d); } if (Cache == null || tileSource.UriTemplate == null || !tileSource.UriTemplate.StartsWith("http")) { cacheName = null; // no tile caching } return(Task.WhenAll(Enumerable.Range(0, numTasks).Select( _ => Task.Run(() => LoadPendingTiles(pendingTiles, tileSource, cacheName))))); } } if (Progress != null && progressLoaded < progressTotal) { Progress.Report(1d); } return(Task.CompletedTask); }
private async Task <bool> DownloadTileImageAsync(Tile tile, Uri uri, string cacheKey) { var success = false; try { using (var response = await TileSource.HttpClient.GetAsync(uri)) { success = response.IsSuccessStatusCode; if (!success) { Debug.WriteLine("TileImageLoader: {0}: {1} {2}", uri, (int)response.StatusCode, response.ReasonPhrase); } else if (TileSource.TileAvailable(response.Headers)) { using (var stream = new MemoryStream()) { await response.Content.CopyToAsync(stream); stream.Seek(0, SeekOrigin.Begin); await SetTileImageAsync(tile, stream); // create BitmapFrame before caching SetCachedImage(cacheKey, stream, GetExpiration(response)); } } } } catch (Exception ex) { Debug.WriteLine("TileImageLoader: {0}: {1}", uri, ex.Message); } return(success); }
private static Task LoadTile(Tile tile, TileSource tileSource) { return(SetTileImage(tile, () => tileSource.LoadImageAsync(tile.XIndex, tile.Y, tile.ZoomLevel))); }
private void LoadPendingTiles(Dispatcher dispatcher, TileSource tileSource, string sourceName) { var imageTileSource = tileSource as ImageTileSource; PendingTile pendingTile; while (pendingTiles.TryDequeue(out pendingTile)) { var tile = pendingTile.Tile; 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.IsAbsoluteUri) { image = LoadImage(uri.OriginalString); } else if (uri.Scheme == "file") { image = LoadImage(uri.LocalPath); } else { image = DownloadImage(uri, CacheKey(sourceName, tile)) ?? pendingTile.CachedImage; // use possibly cached image if download failed } } } if (image != null) { dispatcher.BeginInvoke(new Action<Tile, ImageSource>((t, i) => t.SetImage(i)), tile, image); } else { tile.SetImage(null); } } Interlocked.Decrement(ref taskCount); }
private static async Task LoadTileAsync(Tile tile, TileSource tileSource) { var image = await tileSource.LoadImageAsync(tile.XIndex, tile.Y, tile.ZoomLevel).ConfigureAwait(false); await tile.Image.Dispatcher.InvokeAsync(() => tile.SetImage(image)); }
private static string TileKey(TileSource tileSource, Tile tile) { return string.Format("{0:X}/{1:X}/{2:X}/{3:X}", tileSource.GetHashCode(), tile.ZoomLevel, tile.XIndex, tile.Y); }
private async Task LoadPendingTiles(TileSource tileSource, string sourceName) { PendingTile pendingTile; while (pendingTiles.TryDequeue(out pendingTile)) { var tile = pendingTile.Tile; var uri = pendingTile.Uri; var image = pendingTile.Image; var extension = Path.GetExtension(uri.LocalPath); if (string.IsNullOrEmpty(extension) || extension == ".jpeg") { extension = ".jpg"; } var cacheKey = string.Format(@"{0}\{1}\{2}\{3}{4}", sourceName, tile.ZoomLevel, tile.XIndex, tile.Y, extension); var cacheItem = await Cache.GetAsync(cacheKey); var loaded = false; if (cacheItem == null || cacheItem.Expires <= DateTime.UtcNow) { loaded = await DownloadImage(tile, image, uri, cacheKey); } if (!loaded && cacheItem != null && cacheItem.Buffer != null) { using (var stream = new InMemoryRandomAccessStream()) { await stream.WriteAsync(cacheItem.Buffer); await stream.FlushAsync(); stream.Seek(0); await LoadImageFromStream(tile, image, stream); } } } Interlocked.Decrement(ref taskCount); }
private async Task LoadTileImageAsync(Tile tile, TileSource tileSource) { SetTileImage(tile, await tileSource.LoadImageAsync(tile.XIndex, tile.Y, tile.ZoomLevel)); }
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)); } }
private static string TileKey(TileSource tileSource, Tile tile) { return(string.Format("{0:X}/{1:X}/{2:X}/{3:X}", tileSource.GetHashCode(), tile.ZoomLevel, tile.XIndex, tile.Y)); }
private void GetTiles(List<Tile> tiles, Dispatcher dispatcher, TileSource tileSource, string sourceName, bool animateOpacity, int maxDownloads) { if (Cache != null && !string.IsNullOrWhiteSpace(sourceName) && !(tileSource is ImageTileSource) && !tileSource.UriFormat.StartsWith("file:")) { 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 buffer = Cache.Get(TileCache.Key(sourceName, tile)) as byte[]; var image = CreateImage(buffer); if (image == null) { pendingTiles.Enqueue(tile); // not yet cached } else if (CacheUpdateAge > TimeSpan.Zero && TileCache.CreationTime(buffer) + CacheUpdateAge < DateTime.UtcNow) { dispatcher.Invoke(setImageAction, tile, image); // synchronously before enqueuing outdatedTiles.Add(tile); // update outdated cache } else { dispatcher.BeginInvoke(setImageAction, tile, image); } } tiles = outdatedTiles; // enqueue outdated tiles after current tiles } 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)); } }
private void GetTiles(IEnumerable<Tile> tiles, Dispatcher dispatcher, TileSource tileSource, string sourceName, int maxDownloads) { var useCache = Cache != null && !string.IsNullOrEmpty(sourceName) && !(tileSource is ImageTileSource) && !tileSource.UriFormat.StartsWith("file:"); foreach (var tile in tiles) { BitmapSource cachedImage = null; if (useCache && GetCachedImage(CacheKey(sourceName, tile), out cachedImage)) { dispatcher.BeginInvoke(new Action<Tile, ImageSource>((t, i) => t.SetImage(i)), tile, cachedImage); } else { pendingTiles.Enqueue(new PendingTile(tile, cachedImage)); } } var newTaskCount = Math.Min(pendingTiles.Count, maxDownloads) - taskCount; while (newTaskCount-- > 0) { Interlocked.Increment(ref taskCount); Task.Run(() => LoadPendingTiles(dispatcher, tileSource, sourceName)); } }
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, tile.Rotation); if (uri != null) { if (uri.Scheme == "file") // create from FileStream because creating from URI leaves the file open { image = CreateImage(uri.LocalPath); } else { buffer = DownloadImage(uri); image = CreateImage(buffer); } } } if (image != null || !tile.HasImageSource) // set null image if tile does not yet have an ImageSource { dispatcher.BeginInvoke(setImageAction, tile, image); } if (image != null && buffer != null && Cache != null && !string.IsNullOrWhiteSpace(sourceName)) { Cache.Set(TileCache.Key(sourceName, tile), buffer, new CacheItemPolicy { SlidingExpiration = CacheExpiration }); } } Interlocked.Decrement(ref threadCount); }