Ressources d'image WPF

409

Je viens d'un environnement principalement Web et un peu Windows Forms. Pour un nouveau projet, nous utiliserons WPF. L'application WPF aura besoin de 10 à 20 petites icônes et images à des fins d'illustration. Je pense à les stocker dans l'assembly en tant que ressources intégrées. Est-ce la bonne façon de procéder?

Comment spécifier dans XAML qu'un contrôle Image doit charger l'image à partir d'une ressource intégrée?

driis
la source

Réponses:

473

Si vous allez utiliser l'image à plusieurs endroits, cela vaut la peine de charger les données d'image une seule fois dans la mémoire, puis de les partager entre tous les Imageéléments.

Pour ce faire, créez un en BitmapSource tant que ressource quelque part:

<BitmapImage x:Key="MyImageSource" UriSource="../Media/Image.png" />

Ensuite, dans votre code, utilisez quelque chose comme:

<Image Source="{StaticResource MyImageSource}" />

Dans mon cas, j'ai constaté que je devais définir le Image.pngfichier pour avoir une action de génération Resourceplutôt que juste Content. Cela provoque le transport de l'image dans votre assemblage compilé.

Drew Noakes
la source
6
Serait-il possible de le faire de manière dynamique? Si j'ai un nombre différent d'images que je voudrais charger au démarrage, pourrais-je créer une BitmapSource par image et y faire référence de la même manière que ci-dessus?
Becky Franklin
2
@Becky - Oui, mais si vous voulez vous y référer dans Xaml, vous devrez peut-être utiliser l' DynamicResourceextension de balisage au lieu de StaticResource, en supposant que vous connaissiez les clés au moment de la compilation. Dans WPF, vous pouvez créer des dictionnaires de ressources lors de l'exécution. En fait, c'est ce qui se passe lorsque vous chargez un document Xaml, c'est juste que vous ne voyez pas l'équivalent C #.
Drew Noakes
9
Quelque chose que j'ai frappé: si vous ajoutez votre ressource d'image à un dictionnaire de ressources, n'oubliez pas de vous référer à ce dictionnaire d'image dans le XAML pour votre composant. Quelque chose comme: <UserControl.Resources> <ResourceDictionary> <ResourceDictionary.MergedDictionaries> <ResourceDictionary Source = "Dictionary1.xaml" /> </ResourceDictionary.MergedDictionaries> </ResourceDictionary> </UserControl.Resources>
Dan Mitchell
4
J'ajoute généralement Width="{Binding Source.PixelWidth, RelativeSource={RelativeSource Self}}" au Image, car sinon je vois souvent des images devenir grotesquement agrandies pour une raison quelconque (comme des icônes 16x16 étirées pour quelque chose qui ressemble à 200x200 pixels).
OR Mapper
5
J'ai trouvé que si BitmapImage est déclaré dans le référentiel de ressources d'un assembly référencé, UriSource doit être un packURI pour que cela fonctionne. Sinon, vous constaterez que vous pouvez voir l'image dans votre éditeur xaml dans VS mais aucune image lors du débogage. Pack URIS
échouée le
175

J'ai trouvé que la meilleure pratique d'utilisation d'images, de vidéos, etc. est:

  • Changez vos fichiers "Build action" en "Content" . Assurez-vous de cocher Copier dans le répertoire de construction .
    • Trouvé dans le menu "Clic droit" de la fenêtre de l'Explorateur de solutions.
  • Source d'image au format suivant:
    • "/ « YourAssemblyName » ; composant /« YourPath »/ « YourImage.png » "

Exemple

<Image Source="/WPFApplication;component/Images/Start.png" />

Avantages:

  • Les fichiers ne sont pas intégrés à l'assemblage.
    • Le gestionnaire de ressources soulèvera des problèmes de dépassement de mémoire avec trop de ressources (au moment de la génération).
  • Peut être appelé entre les assemblys.
Nuno Rodrigues
la source
36
Cette même approche fonctionne si vous incorporez la ressource dans l'assembly, mais vous devez définir "Build Action" sur "Resource".
Ashley Davis
5
Fonctionne, merci. Une remarque pour les autres: "composant" est requis "tel quel", "Images" est un chemin relatif de png dans le projet. C'est-à-dire que l'image placée à la racine sera "<Image Source =" / WPFApplication; component / Start.png "/>"
Badiboy
3
Un exemple de la façon de procéder en C # serait bien. (Ce n'est pas un URI valide, il ne peut donc pas être utilisé lors de la construction d'un BitmapImage.)
Vaccano
5
Alors, comment faites-vous si le fichier est défini sur Embedded Resource? Cela ne semble pas fonctionner. Et je ne veux pas inclure l'image dans mon projet deux fois. (Je l'utilise déjà comme une ressource intégrée.)
BrainSlugs83
2
Où et comment le «composant» entre-t-il sur le chemin. Cela fait-il partie d'une spécification?
Triynko
46

Certaines personnes demandent à faire cela dans le code et à ne pas obtenir de réponse.

Après avoir passé de nombreuses heures à chercher, j'ai trouvé une méthode très simple, je n'ai trouvé aucun exemple et je partage donc la mienne ici qui fonctionne avec les images. (le mien était un .gif)

Sommaire:

Il renvoie un BitmapFrame que les "destinations" ImageSource semblent aimer.

Utilisation:

doGetImageSourceFromResource ("[YourAssemblyNameHere]", "[YourResourceNameHere]");

Méthode:

static internal ImageSource doGetImageSourceFromResource(string psAssemblyName, string psResourceName)
{
    Uri oUri = new Uri("pack://application:,,,/" +psAssemblyName +";component/" +psResourceName, UriKind.RelativeOrAbsolute);
    return BitmapFrame.Create(oUri);
}

Apprentissage:

D'après mes expériences, la chaîne de pack n'est pas le problème, vérifiez vos flux et surtout si la lire la première fois a mis le pointeur à la fin du fichier et vous devez le remettre à zéro avant de relire.

J'espère que cela vous épargnera les nombreuses heures que je souhaite que cette pièce ait pour moi!

Craig
la source
44

Dans le code pour charger une ressource dans l'assembly en cours d'exécution où mon image Freq.pngétait dans le dossier Iconset 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:

/// <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 (en supposant que vous placez la fonction dans une classe ResourceHelper):

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

Remarque : voir les URI du pack MSDN dans WPF :
pack://application:,,,/ReferencedAssembly;component/Subfolder/ResourceFile.xaml

Eric Ouellet
la source
new Uri throws Invalid URI: Invalid port specified.
Steve
Avez-vous l'uri incriminé?
Eric Ouellet
1
même uri que le vôtre, sauf que le mien s'exécutait dans un WPF hébergé par Winform. Et le schéma "pack" n'était pas encore enregistré lorsque j'ai appelé le nouvel Uri.
Steve
1
Oups ... il est probablement retiré pour gagner WPF hébergé. Je suis désolé. Je n'essaierai pas de le réparer car je pense que ce n'est pas un usage très courant. Bonne chance!
Eric Ouellet
Dans mon cas, l'utilisation a new Uri(@"pack://application:,,,/" + pathInApplication)également fait l'affaire.
Adam Calvet Bohl
40

Oui, c'est la bonne façon.

Vous pouvez utiliser l'image dans le fichier de ressources en utilisant simplement le chemin:

<Image Source="..\Media\Image.png" />

Vous devez définir l'action de génération du fichier image sur "Ressource".

ema
la source
1
Merci pour cela. Existe-t-il un moyen de faire quelque chose de similaire avec une ImageSource, en chargeant essentiellement l'image une fois dans un dictionnaire de ressources. Je crains que cette approche ne charge les données d'image plusieurs fois en mémoire.
Drew Noakes
2
Ce sera un gâchis lorsque vous aurez besoin de refactoriser votre code. Vous devrez modifier manuellement toutes les références d'image si votre document xaml arrive à changer d'espace de nom. La méthode décrite par Drew Noakes est beaucoup plus fluide et maintenable.
Kasper Holdum
14

Description complète de l'utilisation des ressources: ressources d' application, contenu et fichiers de données WPF

Et comment les référencer, lisez «Pack URIs in WPF».

En bref, il existe même des moyens de référencer les ressources des assemblys référencés / référencés.

techfan
la source
Le lien semble être bien vivant (bien qu'il indique "Cette documentation est archivée et n'est pas maintenue." ).
Peter Mortensen
5
  1. Visual Studio 2010 Professional SP1.
  2. Profil client .NET Framework 4.
  3. Image PNG ajoutée comme ressource sur les propriétés du projet.
  4. Nouveau fichier dans le dossier Ressources créé automatiquement.
  5. Créez un ensemble d'actions sur la ressource.

Cela a fonctionné pour moi:

<BitmapImage x:Key="MyImageSource" UriSource="Resources/Image.png" />
JoanComasFdz
la source
3

Si vous utilisez Blend , pour le rendre plus facile et sans difficulté à obtenir le chemin d'accès correct pour l' attribut Source , faites simplement glisser et déposez l'image du panneau Projet sur le concepteur.

user42467
la source
3

Oui, c'est la bonne façon. Vous pouvez utiliser des images dans le fichier de ressources en utilisant un chemin:

<StackPanel Orientation="Horizontal">
    <CheckBox  Content="{Binding Nname}" IsChecked="{Binding IsChecked}"/>
    <Image Source="E:\SWorking\SharePointSecurityApps\SharePointSecurityApps\SharePointSecurityApps.WPF\Images\sitepermission.png"/>
    <TextBlock Text="{Binding Path=Title}"></TextBlock>
</StackPanel>
Sanjay Ranavaya
la source
-5

Les éléments suivants ont fonctionné et les images à définir sont les ressources dans les propriétés:

    var bitmapSource = Imaging.CreateBitmapSourceFromHBitmap(MyProject.Properties.Resources.myImage.GetHbitmap(),
                                      IntPtr.Zero,
                                      Int32Rect.Empty,
                                      BitmapSizeOptions.FromEmptyOptions());
    MyButton.Background = new ImageBrush(bitmapSource);
img_username.Source = bitmapSource;
Raghulan Gowthaman
la source
5
Aucune infraction mais cela sent la mauvaise pratique.
ShloEmi