2009-02-20 6 views
45

J'ai un problème étrange avec WPF, je chargeais des images du disque à l'exécution et les ajoutais à un conteneur StackView. Cependant, les images n'étaient pas affichées. Après un peu de débogage, j'ai trouvé l'astuce, mais ça n'a vraiment aucun sens. Je l'ai fait une petite application de démonstration pour identifier le problème:Chargement dynamique des images dans WPF

Créer un nouveau projet WPF et coller le code comme suit:

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, coller ci-dessous usings par défaut :

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

Copier une image dans le dossier bin/Debug et l'appeler 'picture.jpg'

Ce programme n'affiche rien, à moins que la ligne commentée ne soit décommentée. Est-ce que quelqu'un peut expliquer ce que je fais mal, ou pourquoi cela se produit-il? Si vous supprimez l'image et lancez le programme, elle génère une exception sur la ligne 'int q = ...'. Si cette ligne est commentée, le programme s'exécute sans exceptions même si aucune image n'est présente. Chargement d'une image seulement si nessesary a du sens, mais alors l'image doit être chargée lorsque j'ajoute le contrôle Image au StackPanel.

Des idées? Editer: En passant, si vous ajoutez l'image en tant que ressource, la ligne 'int q = ..' n'est pas nécessaire.

Répondre

90

C'est parce que la création a été retardée. Si vous souhaitez que l'image soit chargée immédiatement, vous pouvez simplement ajouter ce code dans la phase d'initialisation.

src.CacheOption = BitmapCacheOption.OnLoad;

comme ceci:

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

J'ai testé ça et ça marche. Cela prouve cependant qu'il y a un bug dans le framework, car si vous définissez CacheOption sur OnDemand, l'image n'est jamais chargée même si elle doit être chargée avant d'être affichée à l'écran. Merci pour le pourboire, je n'ai pas remarqué cette propriété. +1;) –

+0

M'a aidé aussi :) –

+0

... et moi aussi :) –

6

Ceci est un comportement étrange et bien que je suis incapable de dire pourquoi cela se produit, je peux recommander quelques options.

D'abord, une observation. Si vous incluez l'image en tant que contenu dans VS et la copiez dans le répertoire de sortie, votre code fonctionne. Si l'image est marquée comme None dans VS et que vous la copiez, cela ne fonctionne pas.

Solution 1: FileStream

L'objet BitmapImage accepte un UriSource ou StreamSource comme paramètre. Utilisons StreamSource à la place.

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

Problème: le flux reste ouvert. Si vous le fermez à la fin de cette méthode, l'image n'apparaîtra pas. Cela signifie que le fichier reste verrouillé en écriture sur le système.

Solution 2: MemoryStream

Ceci est essentiellement solution 1 mais vous lisez le fichier dans un flux de mémoire et passer ce flux de mémoire comme argument.

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

Maintenant, vous êtes en mesure de modifier le fichier sur le système, si cela est nécessaire.

0

Vous pouvez essayer de fixer des gestionnaires à divers événements de BitmapImage:

Ils pourraient vous dire un peu plus sur ce qui se passe, dans la mesure l'image est concernée.

+0

J'ai essayé cela, aucun des événements ont été déclenchés. J'utilise la même image pour tous les tests, donc il n'y a rien à tordre avec l'image btw;) –

9

Dans le code pour charger des ressources dans l'ensemble d'exécution lorsque mon image « Freq.png » était dans le dossier « Icônes » et défini comme « ressources ».

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

J'ai aussi fait fonction si quelqu'un le voudrait ...

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

Utilisation:

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

votre solution est beaucoup plus professionnelle merci –

0

Voici la méthode d'extension pour charger une image à partir URI:

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

Exemple d'utilisation:

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

Simple comme ça!

Questions connexes