Skip to main content
 首页 » 编程设计

c#之在 C# 中异步加载 BitmapImage

2024年01月26日52lonelyxmas

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

主窗口代码

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: "Must create DependencySource on same Thread as the DependencyObject."

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

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

我做错了什么?

请您参考如下方法:

当在后台线程中创建 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),这无论如何都会提供更大的灵 active 。


无需任何手动下载的替代方法可能如下所示。它也不需要卡住 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; 
}