using Avalonia.Data.Converters; using Avalonia.Media.Imaging; using System; using System.Collections.Concurrent; using System.Globalization; using System.IO; using System.Threading.Tasks; namespace AuroraDesk.Presentation.Converters; /// /// 图片相关转换器 /// public static class ImageConverters { /// /// 文件路径到图片源的转换器 /// public static readonly IValueConverter FilePathToImageSourceConverter = new FilePathToImageSourceConverter(); /// /// 大于零的转换器(用于数字到布尔值) /// public static readonly IValueConverter IsGreaterThanZeroConverter = new IsGreaterThanZeroConverter(); /// /// 反转布尔值转换器 /// public static readonly IValueConverter InvertedBoolConverter = new InvertedBoolConverter(); /// /// 是否为零的转换器 /// public static readonly IValueConverter IsZeroConverter = new IsZeroConverter(); } /// /// 文件路径到图片源转换器(异步加载缩略图,类似 Windows 11) /// 特点: /// 1. 异步加载,不阻塞UI线程 /// 2. 只加载缩略图(184x184),不加载完整图片 /// 3. 缓存已加载的缩略图,避免重复加载 /// 4. 延迟加载,只在需要时才加载 /// public class FilePathToImageSourceConverter : IValueConverter { // 缩略图缓存(线程安全) private static readonly ConcurrentDictionary _thumbnailCache = new(); // 正在加载的任务字典,避免重复加载 private static readonly ConcurrentDictionary> _loadingTasks = new(); // 缩略图大小(与UI中的184x184一致) private const int ThumbnailSize = 184; public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture) { if (value is string filePath && !string.IsNullOrEmpty(filePath) && File.Exists(filePath)) { // 先返回 null,让 UI 显示占位符 // 然后异步加载缩略图 _ = LoadThumbnailAsync(filePath); return null; // 初始返回 null,异步加载完成后通过绑定更新 } return null; } /// /// 异步加载缩略图 /// private static async Task LoadThumbnailAsync(string filePath) { // 检查缓存 if (_thumbnailCache.TryGetValue(filePath, out var cached)) { return cached; } // 检查是否正在加载 if (_loadingTasks.TryGetValue(filePath, out var loadingTask)) { return await loadingTask; } // 创建新的加载任务 var task = Task.Run(async () => { try { // 在后台线程加载图片 using var imageStream = File.OpenRead(filePath); var bitmap = new Bitmap(imageStream); // 如果图片尺寸小于缩略图尺寸,直接返回原图(克隆) if (bitmap.PixelSize.Width <= ThumbnailSize && bitmap.PixelSize.Height <= ThumbnailSize) { // 克隆位图以避免释放原始资源 using var memoryStream = new MemoryStream(); bitmap.Save(memoryStream); memoryStream.Position = 0; var result = new Bitmap(memoryStream); _thumbnailCache.TryAdd(filePath, result); return result; } // 创建缩略图(保持宽高比) var sourceSize = bitmap.PixelSize; var scale = Math.Min( (double)ThumbnailSize / sourceSize.Width, (double)ThumbnailSize / sourceSize.Height); var thumbnailWidth = (int)(sourceSize.Width * scale); var thumbnailHeight = (int)(sourceSize.Height * scale); // 创建缩略图 var thumbnail = bitmap.CreateScaledBitmap( new Avalonia.PixelSize(thumbnailWidth, thumbnailHeight), Avalonia.Media.Imaging.BitmapInterpolationMode.HighQuality); // 释放原图资源 bitmap.Dispose(); // 缓存缩略图 _thumbnailCache.TryAdd(filePath, thumbnail); return thumbnail; } catch { // 加载失败,缓存 null 避免重复尝试 _thumbnailCache.TryAdd(filePath, null); return null; } finally { // 移除加载任务 _loadingTasks.TryRemove(filePath, out _); } }); _loadingTasks.TryAdd(filePath, task); return await task; } /// /// 获取缩略图(同步方法,用于异步绑定) /// public static Bitmap? GetThumbnail(string filePath) { if (_thumbnailCache.TryGetValue(filePath, out var cached)) { return cached; } // 触发异步加载 _ = LoadThumbnailAsync(filePath); return null; } /// /// 清除缓存 /// public static void ClearCache() { foreach (var bitmap in _thumbnailCache.Values) { bitmap?.Dispose(); } _thumbnailCache.Clear(); _loadingTasks.Clear(); } public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture) { throw new NotImplementedException(); } } /// /// 大于零转换器 /// public class IsGreaterThanZeroConverter : IValueConverter { public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture) { if (value is int intValue) { return intValue > 0; } if (value is long longValue) { return longValue > 0; } if (value is double doubleValue) { return doubleValue > 0; } return false; } public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture) { throw new NotImplementedException(); } } /// /// 反转布尔值转换器 /// public class InvertedBoolConverter : IValueConverter { public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture) { if (value is bool boolValue) { return !boolValue; } return true; } public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture) { if (value is bool boolValue) { return !boolValue; } return false; } } /// /// 是否为零转换器 /// public class IsZeroConverter : IValueConverter { public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture) { if (value is int intValue) { return intValue == 0; } if (value is long longValue) { return longValue == 0; } if (value is double doubleValue) { return doubleValue == 0; } return true; } public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture) { throw new NotImplementedException(); } }