2009-02-20 14 views
45

Ich habe ein seltsames Problem mit WPF. Ich habe Bilder von der Festplatte zur Laufzeit geladen und sie zu einem StackView-Container hinzugefügt. Die Bilder wurden jedoch nicht angezeigt. Nach einigem Debugging habe ich den Trick gefunden, aber es macht wirklich keinen Sinn. Ich habe eine kleine Demo-Anwendung, das Problem zu identifizieren:Dynamisches Laden von Bildern in WPF

Erstellen Sie ein neues WPF-Projekt und Einfügen von Code wie folgt:

XAML:

<Window x:Class="wpfBug.Window1" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    Title="Window1" Height="300" Width="300" Loaded="Window_Loaded"> 
    <StackPanel Name="sp"> 
    </StackPanel> 
</Window> 

xaml.cs, Paste unter Standard usings :

namespace wpfBug 
{ 
    /// <summary> 
    /// Interaction logic for Window1.xaml 
    /// </summary> 
    public partial class Window1 : Window 
    { 
     public Window1() 
     { 
      InitializeComponent(); 
     } 

     private void Window_Loaded(object sender, RoutedEventArgs e) 
     { 
      Image i = new Image(); 
      BitmapImage src = new BitmapImage(); 
      src.BeginInit(); 
      src.UriSource = new Uri("picture.jpg", UriKind.Relative); 
      src.EndInit(); 
      i.Source = src; 
      i.Stretch = Stretch.Uniform; 
      //int q = src.PixelHeight;  // Image loads here 
      sp.Children.Add(i); 
     } 
    } 
} 

kopieren ein Bild auf das bin/Debug-Ordner und nennen es 'picture.jpg'

Dieses Programm zeigt nichts an, außer die Kommentarzeile ist unkommentiert.

Kann mir jemand erklären, was ich falsch mache oder warum das passiert? Wenn Sie das Bild entfernen und das Programm ausführen, wird eine Ausnahme in der Zeile 'int q = ...' generiert. Wenn diese Zeile kommentiert ist, wird das Programm ohne Ausnahmen ausgeführt, auch wenn kein Bild vorhanden ist. Ein Bild nur laden, wenn es Sinn macht, aber dann sollte das Bild geladen werden, wenn ich das Image-Steuerelement zum StackPanel hinzufüge.

Irgendwelche ides?

Edit: Übrigens, wenn Sie das Bild als Ressource hinzufügen, wird die 'int q = ..' Zeile nicht benötigt.

Antwort

90

Es ist, weil die Schöpfung verzögert wurde. Wenn Sie möchten, dass das Bild sofort geladen wird, können Sie diesen Code einfach in die init-Phase einfügen.

src.CacheOption = BitmapCacheOption.OnLoad;

wie folgt aus:

src.BeginInit(); 
src.UriSource = new Uri("picture.jpg", UriKind.Relative); 
src.CacheOption = BitmapCacheOption.OnLoad; 
src.EndInit(); 
+2

Ich habe das getestet und es funktioniert. Dies beweist jedoch, dass es einen Fehler im Framework gibt, denn wenn Sie CacheOption auf OnDemand setzen, wird das Bild nie geladen, auch wenn es geladen werden sollte, bevor es auf dem Bildschirm angezeigt wird. Danke für den Tipp, ich habe dieses Eigentum nicht bemerkt. +1;) –

+0

hat mir auch geholfen :) –

+0

... und ich auch :) –

6

Das ist merkwürdiges Verhalten und obwohl ich nicht sagen kann, warum das passiert, kann ich einige Optionen empfehlen.

Zuerst eine Beobachtung. Wenn Sie das Bild als Inhalt in VS einschließen und es in das Ausgabeverzeichnis kopieren, funktioniert Ihr Code. Wenn das Bild in VS als Keine markiert ist und Sie es kopieren, funktioniert es nicht.

Lösung 1: Filestream

Das Bitmapobjekt akzeptiert einen UriSource oder Stream als Parameter. Lassen Sie uns stattdessen StreamSource verwenden.

 FileStream stream = new FileStream("picture.png", FileMode.Open, FileAccess.Read); 
     Image i = new Image(); 
     BitmapImage src = new BitmapImage(); 
     src.BeginInit(); 
     src.StreamSource = stream; 
     src.EndInit(); 
     i.Source = src; 
     i.Stretch = Stretch.Uniform; 
     panel.Children.Add(i); 

Das Problem: Strom bleibt offen. Wenn Sie es am Ende dieser Methode schließen, wird das Bild nicht angezeigt. Dies bedeutet, dass die Datei auf dem System schreibgeschützt bleibt.

Lösung 2: Memory

Dies ist im Grunde Lösung 1, aber Sie die Datei in einen Speicher-Stream lesen und diesen Speicher-Stream als Argument übergeben.

 MemoryStream ms = new MemoryStream(); 
     FileStream stream = new FileStream("picture.png", FileMode.Open, FileAccess.Read); 
     ms.SetLength(stream.Length); 
     stream.Read(ms.GetBuffer(), 0, (int)stream.Length); 

     ms.Flush(); 
     stream.Close(); 

     Image i = new Image(); 
     BitmapImage src = new BitmapImage(); 
     src.BeginInit(); 
     src.StreamSource = ms; 
     src.EndInit(); 
     i.Source = src; 
     i.Stretch = Stretch.Uniform; 
     panel.Children.Add(i); 

Jetzt können Sie die Datei auf dem System ändern, wenn dies etwas, das Sie benötigen.

0

Sie könnten versuchen, Handler für verschiedene Veranstaltungen von Bitmap Befestigung:

Sie könnten Ihnen ein wenig über das, was vor sich geht, so weit das Bild ist betroffen.

+0

Ich habe versucht, dass keines der Ereignisse ausgelöst wurde. Ich benutze das gleiche Bild für alle Tests, also gibt es nichts mit dem Bild wtwt;) –

9

In Code-Ressource in der Ausführung Assembly zu laden, wo mein Bild 'Freq.png' war in den Ordner "Icons" und definiert als "Ressource".

 this.Icon = new BitmapImage(new Uri(@"pack://application:,,,/" 
      + Assembly.GetExecutingAssembly().GetName().Name 
      + ";component/" 
      + "Icons/Freq.png", UriKind.Absolute)); 

Ich habe auch eine Funktion, wenn jemand es möchte ...

/// <summary> 
/// Load a resource WPF-BitmapImage (png, bmp, ...) from embedded resource defined as 'Resource' not as 'Embedded resource'. 
/// </summary> 
/// <param name="pathInApplication">Path without starting slash</param> 
/// <param name="assembly">Usually 'Assembly.GetExecutingAssembly()'. If not mentionned, I will use the calling assembly</param> 
/// <returns></returns> 
public static BitmapImage LoadBitmapFromResource(string pathInApplication, Assembly assembly = null) 
{ 
    if (assembly == null) 
    { 
     assembly = Assembly.GetCallingAssembly(); 
    } 

    if (pathInApplication[0] == '/') 
    { 
     pathInApplication = pathInApplication.Substring(1); 
    } 
    return new BitmapImage(new Uri(@"pack://application:,,,/" + assembly.GetName().Name + ";component/" + pathInApplication, UriKind.Absolute)); 
} 

Verbrauch:

 this.Icon = ResourceHelper.LoadBitmapFromResource("Icons/Freq.png"); 
+0

Ihre Lösung ist viel professioneller dank –

0

Hier ist die Extension-Methode ein Bild von URI zu laden:

public static BitmapImage GetBitmapImage(
    this Uri imageAbsolutePath, 
    BitmapCacheOption bitmapCacheOption = BitmapCacheOption.Default) 
{ 
    BitmapImage image = new BitmapImage(); 
    image.BeginInit(); 
    image.CacheOption = bitmapCacheOption; 
    image.UriSource = imageAbsolutePath; 
    image.EndInit(); 

    return image; 
} 

Anwendungsbeispiel:

Uri _imageUri = new Uri(imageAbsolutePath); 
ImageXamlElement.Source = _imageUri.GetBitmapImage(BitmapCacheOption.OnLoad); 

Einfach so!