在C#中异步加载BitmapImage [英] async load BitmapImage in C#

查看:393
本文介绍了在C#中异步加载BitmapImage的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试异步加载图像.

I'm trying to load a image asynchronously.

MainWindow代码

public partial class MainWindow : Window
{
    private Data data = new Data();
    public MainWindow()
    {
        InitializeComponent();
        this.DataContext = data;
    }
    private async void button_Click(object sender, RoutedEventArgs e)
    {
        data.Image = await Data.GetNewImageAsync();
    }
}

数据类

public class Data : INotifyPropertyChanged
{
    private BitmapImage _Image = new BitmapImage();
    public BitmapImage Image { get { return _Image; } set { _Image = value; OnPropertyChanged("Image"); } }

    public static BitmapImage GetNewImage()
    {
        return new BitmapImage(new Uri("http://www.diseno-art.com/news_content/wp-content/uploads/2012/09/2013-Jaguar-F-Type-1.jpg"));
    }

    public async static Task<BitmapImage> GetNewImageAsync()
    {
        return await Task.Run(() => GetNewImage());
    }

    public event PropertyChangedEventHandler PropertyChanged;
    public void OnPropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }
}

WPF代码

<Button Name="button" Click="button_Click">Image</Button>
<Image Grid.Row="1" Source="{Binding Path=Image, UpdateSourceTrigger=PropertyChanged}"></Image>

问题

我得到了例外:

System.ArgumentException:必须在同一线程上创建DependencySource 作为DependencyObject."

System.ArgumentException: "Must create DependencySource on same Thread as the DependencyObject."

...在此行中: PropertyChanged(this, new PropertyChangedEventArgs(propertyName));

... in this row: PropertyChanged(this, new PropertyChangedEventArgs(propertyName));

但是如果我将BitmapImage更改为字符串,则此代码可以正常工作.

But if i change BitmapImage to string this code works fine.

我在做什么错了?

推荐答案

在后台线程中创建BitmapImage时,必须确保将其冻结后才能在UI线程中使用.

When a BitmapImage is created in a background thread, you have to make sure that it gets frozen before it is used in the UI thread.

您必须像这样自己加载它:

You would have to load it yourself like this:

public static async Task<BitmapImage> GetNewImageAsync(Uri uri)
{
    BitmapImage bitmap = null;
    var httpClient = new HttpClient();

    using (var response = await httpClient.GetAsync(uri))
    {
        if (response.IsSuccessStatusCode)
        {
            using (var stream = new MemoryStream())
            {
                await response.Content.CopyToAsync(stream);
                stream.Seek(0, SeekOrigin.Begin);

                bitmap = new BitmapImage();
                bitmap.BeginInit();
                bitmap.CacheOption = BitmapCacheOption.OnLoad;
                bitmap.StreamSource = stream;
                bitmap.EndInit();
                bitmap.Freeze();
            }
        }
    }

    return bitmap;
}

或更短的BitmapFrame.Create,它将返回已冻结的BitmapSource:

Or shorter with BitmapFrame.Create, which returns an already frozen BitmapSource:

public static async Task<BitmapSource> GetNewImageAsync(Uri uri)
{
    BitmapSource bitmap = null;
    var httpClient = new HttpClient();

    using (var response = await httpClient.GetAsync(uri))
    {
        if (response.IsSuccessStatusCode)
        {
            using (var stream = new MemoryStream())
            {
                await response.Content.CopyToAsync(stream);
                stream.Seek(0, SeekOrigin.Begin);

                bitmap = BitmapFrame.Create(
                    stream, BitmapCreateOptions.None, BitmapCacheOption.OnLoad);
            }
        }
    }

    return bitmap;
}

请注意,第二种方法需要将Image属性的类型更改为BitmapSource(或者更好的是ImageSource),无论如何这将提供更大的灵活性.

Note that the second method requires to change the type of your Image property to BitmapSource (or even better, ImageSource), which would provide greater flexibility anyway.

另一种无需任何手动下载的方法可能如下所示.它也不需要冻结BitmatImage,因为它不是在Task线程中创建的.

An alternative method without any manual download might look like shown below. It also does not require to freeze the BitmatImage, because it is not created in a Task thread.

public static Task<BitmapSource> GetNewImageAsync(Uri uri)
{
    var tcs = new TaskCompletionSource<BitmapSource>();
    var bitmap = new BitmapImage(uri);

    if (bitmap.IsDownloading)
    {
        bitmap.DownloadCompleted += (s, e) => tcs.SetResult(bitmap);
        bitmap.DownloadFailed += (s, e) => tcs.SetException(e.ErrorException);
    }
    else
    {
        tcs.SetResult(bitmap);
    }

    return tcs.Task;
}

这篇关于在C#中异步加载BitmapImage的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆