From f4d43eeb441acd428da2741ab7da4aea21fa62a3 Mon Sep 17 00:00:00 2001 From: ClemensFischer Date: Wed, 10 Sep 2025 20:09:12 +0200 Subject: [PATCH] Moved asynchronous image loading to Tile class --- MapControl/Avalonia/Tile.Avalonia.cs | 52 +++++++++------ .../Avalonia/TileImageLoader.Avalonia.cs | 18 ----- MapControl/Shared/Tile.cs | 18 +---- MapControl/Shared/TileImageLoader.cs | 17 +---- MapControl/UWP/MapControl.UWP.csproj | 1 - MapControl/UWP/TileImageLoader.UWP.cs | 36 ---------- MapControl/WPF/Tile.WPF.cs | 38 +++++++---- MapControl/WPF/TileImageLoader.WPF.cs | 17 ----- MapControl/WinUI/Tile.WinUI.cs | 65 +++++++++++++++---- MapControl/WinUI/TileImageLoader.WinUI.cs | 36 ---------- 10 files changed, 114 insertions(+), 184 deletions(-) delete mode 100644 MapControl/Avalonia/TileImageLoader.Avalonia.cs delete mode 100644 MapControl/UWP/TileImageLoader.UWP.cs delete mode 100644 MapControl/WPF/TileImageLoader.WPF.cs delete mode 100644 MapControl/WinUI/TileImageLoader.WinUI.cs diff --git a/MapControl/Avalonia/Tile.Avalonia.cs b/MapControl/Avalonia/Tile.Avalonia.cs index accd4eca..e4165039 100644 --- a/MapControl/Avalonia/Tile.Avalonia.cs +++ b/MapControl/Avalonia/Tile.Avalonia.cs @@ -1,33 +1,47 @@ using Avalonia; using Avalonia.Animation; +using Avalonia.Media; using Avalonia.Styling; +using Avalonia.Threading; using System; +using System.Threading.Tasks; namespace MapControl { public partial class Tile { - private void FadeIn() + public async Task LoadImageAsync(Func> loadImageFunc) { - var fadeInAnimation = new Animation - { - Duration = MapBase.ImageFadeDuration, - Children = - { - new KeyFrame - { - KeyTime = TimeSpan.Zero, - Setters = { new Setter(Visual.OpacityProperty, 0d) } - }, - new KeyFrame - { - KeyTime = MapBase.ImageFadeDuration, - Setters = { new Setter(Visual.OpacityProperty, 1d) } - } - } - }; + var image = await loadImageFunc().ConfigureAwait(false); - _ = fadeInAnimation.RunAsync(Image); + await Dispatcher.UIThread.InvokeAsync( + () => + { + Image.Source = image; + + if (image != null && MapBase.ImageFadeDuration > TimeSpan.Zero) + { + var fadeInAnimation = new Animation + { + Duration = MapBase.ImageFadeDuration, + Children = + { + new KeyFrame + { + KeyTime = TimeSpan.Zero, + Setters = { new Setter(Visual.OpacityProperty, 0d) } + }, + new KeyFrame + { + KeyTime = MapBase.ImageFadeDuration, + Setters = { new Setter(Visual.OpacityProperty, 1d) } + } + } + }; + + _ = fadeInAnimation.RunAsync(Image); + } + }); } } } diff --git a/MapControl/Avalonia/TileImageLoader.Avalonia.cs b/MapControl/Avalonia/TileImageLoader.Avalonia.cs deleted file mode 100644 index 6fef3397..00000000 --- a/MapControl/Avalonia/TileImageLoader.Avalonia.cs +++ /dev/null @@ -1,18 +0,0 @@ -using Avalonia.Media; -using Avalonia.Threading; -using System; -using System.Threading; -using System.Threading.Tasks; - -namespace MapControl -{ - public partial class TileImageLoader - { - private static async Task LoadTileImage(Tile tile, Func> loadImageFunc) - { - var image = await loadImageFunc().ConfigureAwait(false); - - await Dispatcher.UIThread.InvokeAsync(() => tile.SetImageSource(image)); - } - } -} diff --git a/MapControl/Shared/Tile.cs b/MapControl/Shared/Tile.cs index 29fb8821..19365850 100644 --- a/MapControl/Shared/Tile.cs +++ b/MapControl/Shared/Tile.cs @@ -1,5 +1,4 @@ -using System; -#if WPF +#if WPF using System.Windows.Controls; using System.Windows.Media; #elif UWP @@ -25,25 +24,12 @@ namespace MapControl Column = ((x % columnCount) + columnCount) % columnCount; } + public Image Image { get; } = new Image { Stretch = Stretch.Fill }; public int ZoomLevel { get; } public int X { get; } public int Y { get; } public int Column { get; } public int Row => Y; - - public Image Image { get; } = new Image { Stretch = Stretch.Fill }; - public bool IsPending { get; set; } = true; - - public void SetImageSource(ImageSource image) - { - IsPending = false; - Image.Source = image; - - if (image != null && MapBase.ImageFadeDuration > TimeSpan.Zero) - { - FadeIn(); - } - } } } diff --git a/MapControl/Shared/TileImageLoader.cs b/MapControl/Shared/TileImageLoader.cs index d6028cf5..32d087f5 100644 --- a/MapControl/Shared/TileImageLoader.cs +++ b/MapControl/Shared/TileImageLoader.cs @@ -7,13 +7,6 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading.Tasks; -#if WPF -using System.Windows.Media; -#elif UWP -using Windows.UI.Xaml.Media; -#elif WINUI -using Microsoft.UI.Xaml.Media; -#endif namespace MapControl { @@ -27,7 +20,7 @@ namespace MapControl void CancelLoadTiles(); } - public partial class TileImageLoader : ITileImageLoader + public class TileImageLoader : ITileImageLoader { private static ILogger logger; private static ILogger Logger => logger ?? (logger = ImageLoader.LoggerFactory?.CreateLogger()); @@ -161,9 +154,7 @@ namespace MapControl if (string.IsNullOrEmpty(cacheName)) { - Task LoadImage() => tileSource.LoadImageAsync(tile.Column, tile.Row, tile.ZoomLevel); - - await LoadTileImage(tile, LoadImage).ConfigureAwait(false); + await tile.LoadImageAsync(() => tileSource.LoadImageAsync(tile.Column, tile.Row, tile.ZoomLevel)).ConfigureAwait(false); } else { @@ -175,9 +166,7 @@ namespace MapControl if (buffer?.Length > 0) { - Task LoadImage() => tileSource.LoadImageAsync(buffer); - - await LoadTileImage(tile, LoadImage).ConfigureAwait(false); + await tile.LoadImageAsync(() => tileSource.LoadImageAsync(buffer)).ConfigureAwait(false); } } } diff --git a/MapControl/UWP/MapControl.UWP.csproj b/MapControl/UWP/MapControl.UWP.csproj index 2c6c9eab..7b5e929e 100644 --- a/MapControl/UWP/MapControl.UWP.csproj +++ b/MapControl/UWP/MapControl.UWP.csproj @@ -282,7 +282,6 @@ Tile.WinUI.cs - diff --git a/MapControl/UWP/TileImageLoader.UWP.cs b/MapControl/UWP/TileImageLoader.UWP.cs deleted file mode 100644 index 2f8640d1..00000000 --- a/MapControl/UWP/TileImageLoader.UWP.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System; -using System.Threading; -using System.Threading.Tasks; -using Windows.UI.Core; -using Windows.UI.Xaml.Media; - -namespace MapControl -{ - public partial class TileImageLoader - { - private static async Task LoadTileImage(Tile tile, Func> loadImageFunc) - { - var tcs = new TaskCompletionSource(); - - async void LoadTileImage() - { - try - { - tile.SetImageSource(await loadImageFunc()); - tcs.TrySetResult(null); - } - catch (Exception ex) - { - tcs.TrySetException(ex); - } - } - - if (!await tile.Image.Dispatcher.TryRunAsync(CoreDispatcherPriority.Low, LoadTileImage)) - { - tcs.TrySetCanceled(); - } - - await tcs.Task; - } - } -} diff --git a/MapControl/WPF/Tile.WPF.cs b/MapControl/WPF/Tile.WPF.cs index 80a17988..f0ac1e75 100644 --- a/MapControl/WPF/Tile.WPF.cs +++ b/MapControl/WPF/Tile.WPF.cs @@ -1,4 +1,5 @@ using System; +using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Media; @@ -9,6 +10,30 @@ namespace MapControl { public partial class Tile { + public async Task LoadImageAsync(Func> loadImageFunc) + { + var image = await loadImageFunc().ConfigureAwait(false); + + await Image.Dispatcher.InvokeAsync( + () => + { + Image.Source = image; + + if (image != null && MapBase.ImageFadeDuration > TimeSpan.Zero) + { + if (image is BitmapSource bitmap && !bitmap.IsFrozen && bitmap.IsDownloading) + { + bitmap.DownloadCompleted += BitmapDownloadCompleted; + bitmap.DownloadFailed += BitmapDownloadFailed; + } + else + { + BeginFadeInAnimation(); + } + } + }); + } + private void BeginFadeInAnimation() { var fadeInAnimation = new DoubleAnimation @@ -21,19 +46,6 @@ namespace MapControl Image.BeginAnimation(UIElement.OpacityProperty, fadeInAnimation); } - private void FadeIn() - { - if (Image.Source is BitmapSource bitmap && !bitmap.IsFrozen && bitmap.IsDownloading) - { - bitmap.DownloadCompleted += BitmapDownloadCompleted; - bitmap.DownloadFailed += BitmapDownloadFailed; - } - else - { - BeginFadeInAnimation(); - } - } - private void BitmapDownloadCompleted(object sender, EventArgs e) { var bitmap = (BitmapSource)sender; diff --git a/MapControl/WPF/TileImageLoader.WPF.cs b/MapControl/WPF/TileImageLoader.WPF.cs deleted file mode 100644 index a37c32e9..00000000 --- a/MapControl/WPF/TileImageLoader.WPF.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System; -using System.Threading; -using System.Threading.Tasks; -using System.Windows.Media; - -namespace MapControl -{ - public partial class TileImageLoader - { - private static async Task LoadTileImage(Tile tile, Func> loadImageFunc) - { - var image = await loadImageFunc().ConfigureAwait(false); - - await tile.Image.Dispatcher.InvokeAsync(() => tile.SetImageSource(image)); - } - } -} diff --git a/MapControl/WinUI/Tile.WinUI.cs b/MapControl/WinUI/Tile.WinUI.cs index 00d9027e..a31c9f3d 100644 --- a/MapControl/WinUI/Tile.WinUI.cs +++ b/MapControl/WinUI/Tile.WinUI.cs @@ -1,9 +1,15 @@ -#if UWP +using System; +using System.Threading.Tasks; +#if UWP +using Windows.UI.Core; using Windows.UI.Xaml; +using Windows.UI.Xaml.Media; using Windows.UI.Xaml.Media.Animation; using Windows.UI.Xaml.Media.Imaging; #else +using Microsoft.UI.Dispatching; using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Media; using Microsoft.UI.Xaml.Media.Animation; using Microsoft.UI.Xaml.Media.Imaging; #endif @@ -12,6 +18,50 @@ namespace MapControl { public partial class Tile { + public async Task LoadImageAsync(Func> loadImageFunc) + { + var tcs = new TaskCompletionSource(); + + async void LoadImage() + { + try + { + var image = await loadImageFunc(); + + Image.Source = image; + + if (image != null && MapBase.ImageFadeDuration > TimeSpan.Zero) + { + if (image is BitmapImage bitmap && bitmap.UriSource != null) + { + bitmap.ImageOpened += BitmapImageOpened; + bitmap.ImageFailed += BitmapImageFailed; + } + else + { + BeginFadeInAnimation(); + } + } + + tcs.TrySetResult(null); + } + catch (Exception ex) + { + tcs.TrySetException(ex); + } + } +#if UWP + if (!await Image.Dispatcher.TryRunAsync(CoreDispatcherPriority.Low, LoadImage)) +#else + if (!Image.DispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, LoadImage)) +#endif + { + tcs.TrySetCanceled(); + } + + await tcs.Task; + } + private void BeginFadeInAnimation() { var fadeInAnimation = new DoubleAnimation @@ -29,19 +79,6 @@ namespace MapControl storyboard.Begin(); } - private void FadeIn() - { - if (Image.Source is BitmapImage bitmap && bitmap.UriSource != null) - { - bitmap.ImageOpened += BitmapImageOpened; - bitmap.ImageFailed += BitmapImageFailed; - } - else - { - BeginFadeInAnimation(); - } - } - private void BitmapImageOpened(object sender, RoutedEventArgs e) { var bitmap = (BitmapImage)sender; diff --git a/MapControl/WinUI/TileImageLoader.WinUI.cs b/MapControl/WinUI/TileImageLoader.WinUI.cs deleted file mode 100644 index c526c233..00000000 --- a/MapControl/WinUI/TileImageLoader.WinUI.cs +++ /dev/null @@ -1,36 +0,0 @@ -using Microsoft.UI.Dispatching; -using Microsoft.UI.Xaml.Media; -using System; -using System.Threading; -using System.Threading.Tasks; - -namespace MapControl -{ - public partial class TileImageLoader - { - private static Task LoadTileImage(Tile tile, Func> loadImageFunc) - { - var tcs = new TaskCompletionSource(); - - async void LoadTileImage() - { - try - { - tile.SetImageSource(await loadImageFunc()); - tcs.TrySetResult(); - } - catch (Exception ex) - { - tcs.TrySetException(ex); - } - } - - if (!tile.Image.DispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, LoadTileImage)) - { - tcs.TrySetCanceled(); - } - - return tcs.Task; - } - } -}