Moved asynchronous image loading to Tile class

This commit is contained in:
ClemensFischer 2025-09-10 20:09:12 +02:00
parent 2f9c50fb47
commit f4d43eeb44
10 changed files with 114 additions and 184 deletions

View file

@ -1,13 +1,25 @@
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<Task<IImage>> loadImageFunc)
{
var image = await loadImageFunc().ConfigureAwait(false);
await Dispatcher.UIThread.InvokeAsync(
() =>
{
Image.Source = image;
if (image != null && MapBase.ImageFadeDuration > TimeSpan.Zero)
{
var fadeInAnimation = new Animation
{
@ -29,5 +41,7 @@ namespace MapControl
_ = fadeInAnimation.RunAsync(Image);
}
});
}
}
}

View file

@ -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<Task<IImage>> loadImageFunc)
{
var image = await loadImageFunc().ConfigureAwait(false);
await Dispatcher.UIThread.InvokeAsync(() => tile.SetImageSource(image));
}
}
}

View file

@ -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();
}
}
}
}

View file

@ -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<TileImageLoader>());
@ -161,9 +154,7 @@ namespace MapControl
if (string.IsNullOrEmpty(cacheName))
{
Task<ImageSource> 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<ImageSource> LoadImage() => tileSource.LoadImageAsync(buffer);
await LoadTileImage(tile, LoadImage).ConfigureAwait(false);
await tile.LoadImageAsync(() => tileSource.LoadImageAsync(buffer)).ConfigureAwait(false);
}
}
}

View file

@ -282,7 +282,6 @@
<Link>Tile.WinUI.cs</Link>
</Compile>
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="TileImageLoader.UWP.cs" />
<EmbeddedResource Include="Properties\MapControl.UWP.rd.xml" />
</ItemGroup>
<ItemGroup>

View file

@ -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<Task<ImageSource>> loadImageFunc)
{
var tcs = new TaskCompletionSource<object>();
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;
}
}
}

View file

@ -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<Task<ImageSource>> 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;

View file

@ -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<Task<ImageSource>> loadImageFunc)
{
var image = await loadImageFunc().ConfigureAwait(false);
await tile.Image.Dispatcher.InvokeAsync(() => tile.SetImageSource(image));
}
}
}

View file

@ -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<Task<ImageSource>> loadImageFunc)
{
var tcs = new TaskCompletionSource<object>();
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;

View file

@ -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<Task<ImageSource>> 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;
}
}
}