WinUI3异步绑定转换器加载图片报错,求无需项ViewModel的最优方案
WinUI3 无列表项ViewModel实现带认证异步加载GridView图片方案
针对你遇到的TaskCompletionNotifier<BitmapImage>无法转换为ImageSource的问题,下面提供两种无需为列表项单独创建ViewModel的解决方案,优先推荐更灵活的附加属性方案:
方案1:修正TaskCompletionNotifier绑定逻辑
问题根源
你之前直接将TaskCompletionNotifier<BitmapImage>对象绑定给Image.Source,但WinUI绑定系统无法直接将该类型转换为ImageSource。正确做法是绑定其Result属性(Task完成后会自动触发更新)。
代码实现
1. 完善TaskCompletionNotifier实现(确保支持属性变更通知)
public class TaskCompletionNotifier<T> : INotifyPropertyChanged { public TaskCompletionNotifier(Task<T> task) { Task = task; if (task.IsCompleted) OnTaskCompleted(); else task.ContinueWith(t => OnTaskCompleted(), TaskScheduler.FromCurrentSynchronizationContext()); } public Task<T> Task { get; } public T Result => Task.Status == TaskStatus.RanToCompletion ? Task.Result : default; public TaskStatus Status => Task.Status; public event PropertyChangedEventHandler PropertyChanged; private void OnTaskCompleted() { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Result))); PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Status))); } }
2. 缩略图ID转TaskCompletionNotifier转换器
public class ThumbnailIdToTaskNotifierConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, string language) { if (value is string thumbnailId && !string.IsNullOrEmpty(thumbnailId)) { var loadTask = LoadThumbnailAsync(thumbnailId); return new TaskCompletionNotifier<BitmapImage>(loadTask); } return new TaskCompletionNotifier<BitmapImage>(Task.FromResult<BitmapImage>(null)); } public object ConvertBack(object value, Type targetType, object parameter, string language) => throw new NotImplementedException(); private async Task<BitmapImage> LoadThumbnailAsync(string thumbnailId) { // 替换为你的带认证API调用逻辑 using var client = new HttpClient(); client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", "你的认证Token"); var stream = await client.GetStreamAsync($"https://你的API地址/thumbnails/{thumbnailId}"); var bitmap = new BitmapImage(); await bitmap.SetSourceAsync(stream.AsRandomAccessStream()); return bitmap; } }
3. XAML绑定修正
绑定转换器返回的TaskCompletionNotifier的Result属性:
<Page.Resources> <local:ThumbnailIdToTaskNotifierConverter x:Key="ThumbnailConverter"/> </Page.Resources> <GridView ItemsSource="{Binding YourModelList}"> <GridView.ItemTemplate> <DataTemplate> <Image Width="100" Height="100" Source="{Binding ThumbnailId, Converter={StaticResource ThumbnailConverter}, Path=Result}"/> </DataTemplate> </GridView.ItemTemplate> </GridView>
方案2:使用附加属性(推荐,支持占位/错误处理)
这种方式无需依赖TaskCompletionNotifier,直接通过附加属性封装异步加载逻辑,还能轻松扩展占位图、错误图等功能。
代码实现
1. 图片加载附加属性
public static class ImageLoadExtensions { public static readonly DependencyProperty SourceTaskProperty = DependencyProperty.RegisterAttached( "SourceTask", typeof(object), typeof(ImageLoadExtensions), new PropertyMetadata(null, OnSourceTaskChanged)); public static void SetSourceTask(DependencyObject element, object value) => element.SetValue(SourceTaskProperty, value); public static object GetSourceTask(DependencyObject element) => element.GetValue(SourceTaskProperty); private static async void OnSourceTaskChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { if (d is not Image image) return; // 设置加载占位图 image.Source = new BitmapImage(new Uri("ms-appx:///Assets/Placeholder.png")); try { BitmapImage result = null; if (e.NewValue is Task<BitmapImage> loadTask) { result = await loadTask; } else if (e.NewValue is string thumbnailId && !string.IsNullOrEmpty(thumbnailId)) { result = await LoadThumbnailAsync(thumbnailId); } if (result != null) image.Source = result; } catch (Exception ex) { // 设置错误占位图 image.Source = new BitmapImage(new Uri("ms-appx:///Assets/ErrorImage.png")); Debug.WriteLine($"图片加载失败: {ex.Message}"); } } private static async Task<BitmapImage> LoadThumbnailAsync(string thumbnailId) { // 同方案1的带认证API调用逻辑 using var client = new HttpClient(); client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", "你的认证Token"); var stream = await client.GetStreamAsync($"https://你的API地址/thumbnails/{thumbnailId}"); var bitmap = new BitmapImage(); await bitmap.SetSourceAsync(stream.AsRandomAccessStream()); return bitmap; } }
2. XAML使用
直接绑定缩略图ID到附加属性即可:
<GridView ItemsSource="{Binding YourModelList}"> <GridView.ItemTemplate> <DataTemplate> <Image Width="100" Height="100" local:ImageLoadExtensions.SourceTask="{Binding ThumbnailId}"/> </DataTemplate> </GridView.ItemTemplate> </GridView>
内容的提问来源于stack exchange,提问作者rcbevans
相关产品推荐
相关产品推荐

