You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.19 17:33:24