WPF图像 - 服务器返回禁止(403)

时间:2017-05-16 22:38:53

标签: c# wpf image binding

我有ListView绑定到ObservableCollection。此ListView使用ItemTemplate,它只包含一个Image控件,Source属性绑定到URL字符串。

对于某些网址图片无法加载,因为远程服务器会返回“禁止(403)” - 可以通过向获取图像的HTTP请求添加特定标头来解决此 ,但是问题是我不知道应该如何修改上述请求。

我尝试了两种不同的方法:

  1. 创建IValueConverter对象。我会自己获取URL并获取图像数据 - 将其放入MemoryStream并使用该流初始化BitmapImage对象并将其返回到Image控件。事实证明这种方法非常慢,并且阻止了UI线程。
  2. 创建要绑定到的新属性将返回包含图像数据的字节数组。第一次使用Task调用时,这些数据会被懒惰地初始化。当数据完成下载时,将触发PropertyChanged事件,以便在视觉上更新图像。这种方法没有阻止UI线程,但由于某种原因它非常慢。
  3. 我想知道两件事:

    • 为什么我的方法明显变慢了?

    • 如何直接修改Image控件从远程服务器获取图像的方式而不会影响性能/速度那么多?

    考虑以下示例:

    public byte[] ImageThumbnail 
    {
        get 
        {
            if (img == null) img = GetImage(ImageUrls.Thumbnail);
            return img;
        }
    }
    public byte[] GetImage(string url) {
         HttpClient client = new HttpClient();
         // add some headers
         return client.GetByteArrayAsync(url).Result;
    }
    

    将IsAsync设置为True,将图像绑定到“ImageThumbnail”。与仅将URL直接绑定到Image源相比,在这种情况下,图像的下载速度要慢得多。

1 个答案:

答案 0 :(得分:0)

您应该在ItemTemplate中对Image控件的Source属性使用异步绑定:

<ListBox.ItemTemplate>
    <DataTemplate>
        <Image Source="{Binding Image, IsAsync=True}"/>
    </DataTemplate>
</ListBox.ItemTemplate>

由于现在在后台线程中调用Image属性getter,因此必须通过冻结使返回的ImageSource可以跨线程访问。

public class ImageItem
{
    private string url;

    public ImageItem(string url)
    {
        this.url = url;
    }

    public ImageSource Image
    {
        get
        {
            var image = new BitmapImage();
            var buffer = new WebClient().DownloadData(url);

            using (var stream = new MemoryStream(buffer))
            {
                image.BeginInit();
                image.CacheOption = BitmapCacheOption.OnLoad;
                image.StreamSource = stream;
                image.EndInit();
            }

            image.Freeze();
            return image;
        }
    }
}

使用BitmapFrame代替BitmapImage可能会缩短Image属性。下面显示的BitmapFrame.Create方法已经返回了一个冻结的BitmapFrame。

public ImageSource Image
{
    get
    {
        var buffer = new WebClient().DownloadData(url);

        using (var stream = new MemoryStream(buffer))
        {
            return BitmapFrame.Create(
                stream, BitmapCreateOptions.None, BitmapCacheOption.OnLoad);
        }
    }
}