【问题标题】:async load BitmapImage in C#在 C# 中异步加载 BitmapImage
【发布时间】:2017-10-12 12:16:06
【问题描述】:

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

主窗口代码

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。”

... 在这一行: PropertyChanged(this, new PropertyChangedEventArgs(propertyName));

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

我做错了什么?

【问题讨论】:

  • 请注意,在图像绑定上设置UpdateSourceTrigger=PropertyChanged 是没有意义的。它只对 TwoWay 和 OneWayToSource 绑定有影响。

标签: c# wpf asynchronous


【解决方案1】:

在后台线程中创建 BitmapImage 时,您必须确保它在用于 UI 线程之前被冻结。

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

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:

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),这将提供更大的灵活性。


无需任何手动下载的替代方法可能如下所示。它也不需要冻结 BitmatImage,因为它不是在任务线程中创建的。

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;
}

【讨论】:

  • 不能设置一个bitmap.UriSource 而不是bitmap.StreamSource 以避免手动下载代码(保留所有缓存选项并冻结相同)?
  • 当您想立即冻结 BitmapImage 时不是。您可以注册一个 DownloadCompleted 事件处理程序,并在其中冻结它,但在调用该处理程序之前您无法将其传递给 UI。
  • @Evk 请查看编辑以获取无需手动下载的替代方案。
  • 是的,谢谢,当你提到 DownloadCompleted 处理程序时,我已经想到了这个想法。
猜你喜欢
  • 2010-09-07
  • 2012-10-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-05-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多