2017-10-12 17 views
1

イメージを非同期にロードしようとしています。async load C#のBitmapImage

メイン・ウィンドウコード

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の: "DependencyObjectのと同じスレッド にDependencySourceを作成する必要があります"この行の

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

しかし、私は、文字列へのBitmapImageを変更した場合、このコードは正常に動作します。

私は間違っていますか?

+0

注:Image Bindingに 'UpdateSourceTrigger = PropertyChanged'を設定するのは無意味です。 TwoWayとOneWayToSourceのバインディングにのみ影響します。 – Clemens

答えて

5

バックグラウンドスレッドでBitmapImageを作成する場合、UIスレッドで使用する前にBitmapImageがフリーズしていることを確認する必要があります。

あなたはこのようにそれを自分でロードする必要があります:第二の方法は、タイプを変更することが必要であることを

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

注:すでに凍結したBitmapSourceを返しBitmapFrame.Create

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

たり短くし、あなたのImageプロパティのBitmapSource(またはさらに優れたImageSource)への変換が可能になります。


手動でダウンロードする代わりに、次のような方法があります。 BitmapImageはタスクスレッドで作成されないため、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; 
} 
+0

手動でコードをダウンロードするのを避けるために 'bitmap.StreamSource'の代わりに' bitmap.UriSource'を設定することはできません(すべてのキャッシュオプションを残してフリーズします)? – Evk

+0

BitmapImageをただちにフリーズしたい場合は選択しないでください。フリーズするところでDownloadCompletedイベントハンドラを登録できますが、ハンドラが呼び出される前にUIに渡すことはできませんでした。 – Clemens

+0

@エヴァイク手動ダウンロードなしの代わりに編集を参照してください。 – Clemens