2017-10-12 2 views
1

Ich versuche, ein Bild asynchron zu laden.async laden BitmapImage in C#

Mainwindow Code

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

Datenklasse

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 Code

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

Problem

bekomme ich die Ausnahme:

System.ArgumentException: "Muss DependencySource auf demselben Thema erstellen als DependencyObject"

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

Aber wenn ich in Ordnung dieser Code funktioniert Bitmap bespannen ändern.

Was mache ich falsch?

+0

Als Hinweis, Einstellung 'UpdateSourceTrigger = PropertyChanged' auf die Bildbindung ist sinnlos. Es wirkt sich nur auf TwoWay und OneWayToSource Bindings aus. – Clemens

Antwort

5

Wenn ein BitmapImage in einem Hintergrund-Thread erstellt wird, müssen Sie sicherstellen, dass es eingefroren wird, bevor es im UI-Thread verwendet wird.

würden Sie haben es laden sich wie folgt aus:

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

oder kürzer mit BitmapFrame.Create, die eine bereits eingefroren Bitmap zurück:

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

Beachten Sie, dass die zweite Methode erfordert die Art zu ändern, von Ihrer Image Eigenschaft zu BitmapSource (oder noch besser, ImageSource), die sowieso eine größere Flexibilität bieten würde.


Eine alternative Methode ohne manuellen Download könnte wie folgt aussehen. Das Einfrieren von BitmatImage ist auch nicht erforderlich, da es nicht in einem Task-Thread erstellt wird.

+0

Kann man nicht 'bitmap.UriSource' anstelle von' bitmap.StreamSource' einstellen, um manuellen Code-Download zu vermeiden (alle Cache-Optionen lassen und dieselben einfrieren)? – Evk

+0

Nicht, wenn Sie die BitmapImage sofort einfrieren möchten. Sie könnten einen DownloadCompleted-Event-Handler registrieren, in dem Sie ihn anhalten würden. Sie konnten ihn jedoch nicht an die Benutzeroberfläche übergeben, bevor der Handler aufgerufen wurde. – Clemens

+0

@Evk finden Sie die Bearbeitung für eine Alternative ohne manuellen Download. – Clemens