web-dev-qa-db-fra.com

Chargement dynamique d'images dans WPF

J'ai un problème étrange avec WPF, je chargeais des images du disque au moment de l'exécution et les ajoutais à un conteneur StackView. Cependant, les images ne sont pas affichées. Après un débogage, j'ai trouvé l'astuce, mais cela n'a vraiment aucun sens. J'ai créé une petite application de démonstration pour identifier le problème:

Créez un nouveau projet WPF et collez 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, collez ci-dessous les utilisations 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);
        }
    }
}

Copiez une image dans le dossier bin/Debug et appelez-la 'picture.jpg'

Ce programme n'affiche rien, sauf si la ligne commentée n'est pas commentée.

Quelqu'un peut-il expliquer ce que je fais mal ou pourquoi cela se produit? Si vous supprimez l'image et exécutez le programme, il génère une exception sur la ligne 'int q = ...'. Si cette ligne est commentée, le programme s'exécute sans exception même si aucune image n'est présente. Charger une image uniquement si cela a du sens, mais alors l'image doit être chargée lorsque j'ajoute le contrôle Image au StackPanel.

Des ides?

Modifier: Par ailleurs, si vous ajoutez l'image en tant que ressource, la ligne "int q = .." n'est pas nécessaire.

48
sindre j

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 ça:

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

Dans le code pour charger la ressource dans l'assembly en cours d'exécution où mon image 'Freq.png' était dans le dossier "Icons" et définie comme "Resource".

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

J'ai aussi fait une fonction si quelqu'un le voulait ...

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

Usage:

        this.Icon = ResourceHelper.LoadBitmapFromResource("Icons/Freq.png");
11
Eric Ouellet

C'est un comportement étrange et bien que je ne puisse pas expliquer 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 Aucune dans VS et que vous la recopiez, cela ne fonctionne pas.

Solution 1: FileStream

L'objet BitmapImage accepte une UriSource ou StreamSource comme paramètre. Utilisons plutôt StreamSource.

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

Le problème: le flux reste ouvert. Si vous le fermez à la fin de cette méthode, l'image ne s'affichera pas. Cela signifie que le fichier reste verrouillé en écriture sur le système.

Solution 2: MemoryStream

Il s'agit essentiellement de la solution 1, mais vous lisez le fichier dans un flux de mémoire et passez 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);

Vous pouvez maintenant modifier le fichier sur le système, si c'est quelque chose dont vous avez besoin.

7
Szymon Rozga

Voici la méthode d'extension pour charger une image à partir de l'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);

Aussi simple que cela!

0
J Pollack

Vous pouvez essayer d'attacher des gestionnaires à divers événements de BitmapImage:

Ils pourraient vous en dire un peu plus sur ce qui se passe, en ce qui concerne l'image.

0
Drew Noakes