2009-02-20 26 views
45

Tengo un problema extraño con WPF, estaba cargando imágenes del disco en tiempo de ejecución y agregándolas a un contenedor StackView. Sin embargo, las imágenes no se muestran. Después de algunas depuraciones encontré el truco, pero realmente no tiene ningún sentido. He hecho una pequeña aplicación de demostración para identificar el problema:Carga dinámica de imágenes en WPF

Crear un nuevo proyecto de WPF, y pegar el código de la siguiente manera:

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, pegar a continuación usings por defecto :

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

Copiar una imagen a la carpeta bin/debug y lo llaman 'foto.jpg'

Este programa no muestra nada, a menos que la línea comentada esté descomentada.

¿Alguien puede explicar lo que estoy haciendo mal, o por qué sucede esto? Si elimina la imagen y ejecuta el programa, genera una excepción en la línea 'int q = ...'. Si se comenta esa línea, el programa se ejecuta sin excepciones, incluso si no hay ninguna imagen presente. Cargar una imagen solo si tiene sentido, pero luego la imagen debe cargarse cuando agregue el control de Imagen a StackPanel.

¿Alguna idea?

Editar: Por cierto, si agrega la imagen como recurso, la línea 'int q = ..' no es necesaria.

Respuesta

90

Esto se debe a que la creación se retrasó. Si desea que la imagen se cargue inmediatamente, simplemente puede agregar este código a la fase de inicio.

src.CacheOption = BitmapCacheOption.OnLoad;

así:

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

He probado esto y funciona. Sin embargo, esto demuestra que hay un error en el marco, porque si configura CacheOption en OnDemand, la imagen nunca se cargará aunque se cargue antes de que se muestre en la pantalla. Gracias por el consejo, no noté esa propiedad. +1;) –

+0

Me ayudó :) –

+0

... y yo también :) –

6

Esto es un comportamiento extraño y aunque no puedo decir por qué ocurre esto, puedo recomendar algunas opciones.

Primero, una observación. Si incluye la imagen como contenido en VS y la copia en el directorio de salida, su código funciona. Si la imagen está marcada como None en VS y la copia, no funciona.

Solución 1: FileStream

El objeto BitmapImage acepta una UriSource o StreamSource como un parámetro. Usemos StreamSource en su lugar.

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

El problema: la secuencia permanece abierta. Si lo cierra al final de este método, la imagen no se mostrará. Esto significa que el archivo permanece bloqueado por escritura en el sistema.

Solución 2: MemoryStream

Esta es básicamente la solución 1, pero leer el archivo en una secuencia de memoria y pasar esa corriente de memoria como argumento.

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

Ahora puede modificar el archivo en el sistema, si eso es algo que necesita.

0

Usted podría intentar unir los manipuladores a varios eventos de BitmapImage:

Se le puede decir un poco acerca de lo que está pasando, por lo que la imagen se refiere.

+0

Lo intenté, ninguno de los eventos se desencadenó. Estoy usando la misma imagen para todas las pruebas, por lo que no hay nada que se retuerza con la imagen por cierto;) –

9

En el código de carga de recursos en el conjunto de la ejecución, donde mi imagen 'Freq.png' fue en los "Iconos" de la carpeta y se define como "recursos".

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

También hice una función si alguien lo quisiera ...

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

Uso:

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

su solución es mucho más profesional gracias –

0

Aquí es el método de extensión para cargar una imagen desde 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; 
} 

Muestra de uso:

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

¡Simple como eso!