13 Stimmen

Asynchrones Laden eines BitmapImage in C# mit WPF

Was ist der beste Weg, um asynchron ein BitmapImage in C# mit WPF zu laden?

7voto

Brian Punkte 71

Ich habe mir das gerade angeschaut und musste meinen Senf dazugeben, wenn auch ein paar Jahre nach dem ursprünglichen Beitrag (nur für den Fall, dass jemand anderes nach der gleichen Sache sucht, die ich mir angeschaut habe).

Ich habe eine Bild Steuerelement, dessen Bild im Hintergrund geladen werden muss, mit einer Stream und dann angezeigt.

Das Problem, auf das ich immer wieder gestoßen bin, ist, dass die BitmapSource ist es Stream Quelle und die Quelle Bild Die Kontrollen mussten alle im selben Thread stattfinden.

In diesem Fall führt die Verwendung einer Bindung und die Einstellung IsAsynch = true zu einer threadübergreifenden Ausnahme.

Ein BackgroundWorker ist großartig für WinForms, und Sie können dies in WPF verwenden, aber ich ziehe es vor, die WinForm-Assemblies in WPF zu vermeiden (Aufblähung eines Projekts wird nicht empfohlen, und es ist auch eine gute Faustregel). Dies sollte eine ungültige Querverweis Ausnahme in diesem Fall auch werfen, aber ich habe es nicht getestet.

Es hat sich herausgestellt, dass eine Zeile Code ausreicht, um alle diese Funktionen auszuführen:

//Create the image control
Image img = new Image {HorizontalAlignment = System.Windows.HorizontalAlignment.Stretch, VerticalAlignment = System.Windows.VerticalAlignment.Stretch};

//Create a seperate thread to load the image
ThreadStart thread = delegate
     {
         //Load the image in a seperate thread
         BitmapImage bmpImage = new BitmapImage();
         MemoryStream ms = new MemoryStream();

         //A custom class that reads the bytes of off the HD and shoves them into the MemoryStream. You could just replace the MemoryStream with something like this: FileStream fs = File.Open(@"C:\ImageFileName.jpg", FileMode.Open);
         MediaCoder.MediaDecoder.DecodeMediaWithStream(ImageItem, true, ms);

         bmpImage.BeginInit();
         bmpImage.StreamSource = ms;
         bmpImage.EndInit();

         //**THIS LINE locks the BitmapImage so that it can be transported across threads!! 
         bmpImage.Freeze();

         //Call the UI thread using the Dispatcher to update the Image control
         Dispatcher.BeginInvoke(new ThreadStart(delegate
                 {
                         img.Source = bmpImage;
                         img.Unloaded += delegate 
                                 {
                                         ms.Close();
                                         ms.Dispose();
                                 };

                          grdImageContainer.Children.Add(img);
                  }));

     };

//Start previously mentioned thread...
new Thread(thread).Start();

7 Stimmen

BackgroundWorker ist im System.ComponentModel-Namensraum der System.dll definiert, nicht in Windows Forms, so ist es in Ordnung, es für WPF zu verwenden.

0 Stimmen

Die Verwendung von BackgroundWorker oder ThreadStart verbraucht zu viel Code. Werfen Sie einfach eine ThreadPool.QueueUserWorkItem(_ => { ... }) und hier ist Ihr Thema;

3voto

aku Punkte 118808

Angenommen, Sie verwenden Datenbindung und setzen Binding.IsAsync Eigenschaft auf True zu setzen, scheint ein Standardweg zu sein, um dies zu erreichen. Wenn Sie die Bitmap in der Code-Behind-Datei mit Hintergrund-Thread + Dispatcher-Objekt laden ist ein gemeinsamer Weg, um UI asynchron zu aktualisieren

4 Stimmen

Das BitmapImage muss im UI-Thread erstellt werden: dies führt zu einer Ausnahme...

1 Stimmen

@Jmix90: Es ist nicht klar, auf welchen Teil der Antwort Sie sich beziehen. Wie auch immer: mit Binding.IsAsync erlaubt die Angabe eines Uri/Pfades in einer Image.Source und lassen Sie das Bild automatisch asynchron laden. WPF kümmert sich in diesem Szenario um alle Thread-übergreifenden Probleme. Wenn man Bitmaps in einem Hintergrund-Thread erstellt, muss man einfach Freeze() auf das Bitmap-Objekt, bevor es an den UI-Thread weitergegeben wird. Es wird nur eine Ausnahme geworfen, wenn Sie es falsch machen.

3voto

David Punkte 359

Dadurch können Sie das BitmapImage auf dem UI-Thread erstellen, indem Sie den HttpClient verwenden, um das asynchrone Herunterladen durchzuführen:

private async Task<BitmapImage> LoadImage(string url)
{
    HttpClient client = new HttpClient();

    try
    {
        BitmapImage img = new BitmapImage();
        img.CacheOption = BitmapCacheOption.OnLoad;
        img.BeginInit();
        img.StreamSource = await client.GetStreamAsync(url);
        img.EndInit();
        return img;
    }
    catch (HttpRequestException)
    {
        // the download failed, log error
        return null;
    }
}

2voto

kevindaub Punkte 3154

Um auf aku's Antwort, hier ist ein kleines Beispiel, wo die IsAsync festgelegt werden soll:

ItemsSource="{Binding IsAsync=True,Source={StaticResource ACollection},Path=AnObjectInCollection}"

Genau das würden Sie in XAML tun.

0 Stimmen

Ich verstehe nicht, warum diese Antwort eine -1 bekommen hat, da sie genau das war, wonach ich gesucht habe! Konnte nicht herausfinden, wie man Konvertierung async zu tun. Stellt sich heraus, dass Sie nur müssen Sie IsAsync=True in Ihre Bindung einfügen. Ich danke Ihnen vielmals!

2voto

Cornel Marian Punkte 2313
 BitmapCacheOption.OnLoad

var bmp = await System.Threading.Tasks.Task.Run(() => 
{ 
BitmapImage img = new BitmapImage(); 
img.BeginInit(); 
img.CacheOption = BitmapCacheOption.OnLoad; 
img.UriSource = new Uri(path); 
img.EndInit(); 
ImageBrush brush = new ImageBrush(img); 

}

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X