Définition de la source d'image WPF dans le code


325

J'essaie de définir la source d'une image WPF dans le code. L'image est intégrée en tant que ressource dans le projet. En regardant des exemples, j'ai trouvé le code ci-dessous. Pour une raison quelconque, cela ne fonctionne pas - l'image ne s'affiche pas.

En déboguant, je peux voir que le flux contient les données d'image. Alors, qu'est-ce qui ne va pas?

Assembly asm = Assembly.GetExecutingAssembly();
Stream iconStream = asm.GetManifestResourceStream("SomeImage.png");
PngBitmapDecoder iconDecoder = new PngBitmapDecoder(iconStream, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.Default);
ImageSource iconSource = iconDecoder.Frames[0];
_icon.Source = iconSource;

L'icône est définie quelque chose comme ceci: <Image x:Name="_icon" Width="16" Height="16" />


Si l'image se trouve sur un lecteur local, <Image Source="some_fully_qualified_path">le XAML n'échoue jamais.
Laurie Stearn

@LaurieStearn, le fait est que nous ne connaissons pas le chemin et avons besoin de code pour le déterminer. En tant que nouveau dans la programmation de l'interface graphique Windows, je dois admettre que WinForms semble plus attrayant que cette merde XAML.
Alex Jansen

Réponses:


416

Après avoir eu le même problème que vous et avoir fait quelques lectures, j'ai découvert la solution - Pack URIs .

J'ai fait ce qui suit dans le code:

Image finalImage = new Image();
finalImage.Width = 80;
...
BitmapImage logo = new BitmapImage();
logo.BeginInit();
logo.UriSource = new Uri("pack://application:,,,/AssemblyName;component/Resources/logo.png");
logo.EndInit();
...
finalImage.Source = logo;

Ou plus court, en utilisant un autre constructeur BitmapImage:

finalImage.Source = new BitmapImage(
    new Uri("pack://application:,,,/AssemblyName;component/Resources/logo.png"));

L'URI est divisé en plusieurs parties:

  • Autorité: application:///
  • Chemin: nom d'un fichier de ressources compilé dans un assembly référencé. Le chemin doit être conforme au format suivant:AssemblyShortName[;Version][;PublicKey];component/Path

    • AssemblyShortName: le nom court de l'assembly référencé.
    • ; Version [facultatif]: la version de l'assembly référencé qui contient le fichier de ressources. Ceci est utilisé lorsque deux ou plusieurs assemblys référencés portant le même nom abrégé sont chargés.
    • ; PublicKey [facultatif]: la clé publique qui a été utilisée pour signer l'assembly référencé. Ceci est utilisé lorsque deux ou plusieurs assemblys référencés portant le même nom abrégé sont chargés.
    • ; composant: spécifie que l'assembly auquel il est fait référence est référencé à partir de l'assembly local.
    • / Path: le nom du fichier de ressources, y compris son chemin, par rapport à la racine du dossier de projet de l'assembly référencé.

Les trois barres obliques application:doivent ensuite être remplacées par des virgules:

Remarque: Le composant d'autorisation d'un URI de pack est un URI intégré qui pointe vers un package et doit être conforme à la RFC 2396. En outre, le caractère "/" doit être remplacé par le caractère "," et les caractères réservés tels que "%" et "?" doit être échappé. Voir l'OPC pour plus de détails.

Et bien sûr, assurez-vous de définir l'action de génération sur votre image Resource.


Oui, c'est la solution que j'ai trouvée après quelques essais et erreurs. Merci pour l'explication approfondie. Réponse acceptée!
Torbjørn

Je ne sais pas pourquoi cela était nécessaire, et pourquoi les autres réponses n'ont pas fonctionné pour moi ...
Torbjørn

les autres réponses dans cette question et d'autres n'ont pas fonctionné pour moi non plus. Celui-ci fonctionne parfaitement. Merci.
Thomas Stock

9
D'accord, mais n'y a-t-il pas une méthode ou une classe que l'analyseur XAML utilise pour convertir la chaîne de chemin d'accès simple en ImageSource? Ne pourrions-nous pas simplement utiliser cela?
Qwertie

1
Je vois souvent cet extrait copié dans d'autres publications, où les gens ignorent généralement l'existence du BitmapImage(Uri)constructeur, ce qui évite d'avoir à appeler BeginInit et EndInit. Je l'ai ajouté ici.
Clemens

175
var uriSource = new Uri(@"/WpfApplication1;component/Images/Untitled.png", UriKind.Relative);
foo.Source = new BitmapImage(uriSource);

Cela chargera une image appelée "Untitled.png" dans un dossier appelé "Images" avec son "Build Action" défini sur "Resource" dans un assembly appelé "WpfApplication1".


16
Merci pour ça. Un problème qui m'a déclenché en tant que noob à wpf, l'image doit être marquée comme ressource pour que cela fonctionne.
si618

4
Cette solution m'a donné une exception, j'ai donc essayé la solution de Jared Harley et cela a fonctionné ...
Sangram Nandkhile

2
la chose la plus importante est "UriKind.RelativeOrAbsolute" - d'autres exemples ont toujours levé des exceptions
Sven

9
var uriSource = new Uri("Images/Untitled.png", UriKind.Relative);serait suffisant
Hamzeh Soboh

... et beaucoup plus facile. Merci Hamzeh
pStan

74

C'est un peu moins de code et peut être fait sur une seule ligne.

string packUri = "pack://application:,,,/AssemblyName;component/Images/icon.png";
_image.Source = new ImageSourceConverter().ConvertFromString(packUri) as ImageSource;

8
C'est certainement la meilleure réponse, en utilisant la propre implémentation de
.NET

que faire si nous devons définir la hauteur et la largeur de l'image, c'est-à-dire icon.png? Parce qu'en utilisant _image.height et _image.width, cela définit la fenêtre d'image et non l'image réelle, c'est-à-dire icon.png.
secretgenes

41

Très facile:

Pour définir dynamiquement l'image d'un élément de menu, procédez uniquement comme suit:

MyMenuItem.ImageSource = 
    new BitmapImage(new Uri("Resource/icon.ico",UriKind.Relative));

... alors que "icon.ico" peut être localisé partout (il se trouve actuellement dans le répertoire 'Resources') et doit être lié en tant que Resource ...


2
Plus court = mieux. J'ai dû le définir comme @ "/ folder / image.png" mais cela a bien fonctionné. Merci!
gjvdkamp

3
Lorsque j'attribue la source d'images, elle apparaît simplement en blanc :(
HaloMediaz

@HaloMediaz chemin peut être faux .... par exemple, vous avez des ressources mais vous pouvez l'écrire Ressource
Rouzbeh Zarandi

Cela a fonctionné pour moi, et beaucoup plus simple que les URI absolus dans les autres réponses.
Psiloc

16

Vous pouvez également réduire cela à une seule ligne. C'est le code que j'ai utilisé pour définir l'icône de ma fenêtre principale. Il suppose que le fichier .ico est marqué comme contenu et qu'il est copié dans le répertoire de sortie.

 this.Icon = new BitmapImage(new Uri("Icon.ico", UriKind.Relative));

16

Manière la plus simple:

var uriSource = new Uri("image path here");
image1.Source = new BitmapImage(uriSource);

15

As-tu essayé:

Assembly asm = Assembly.GetExecutingAssembly();
Stream iconStream = asm.GetManifestResourceStream("SomeImage.png");
BitmapImage bitmap = new BitmapImage();
bitmap.BeginInit();
bitmap.StreamSource = iconStream;
bitmap.EndInit();
_icon.Source = bitmap;

13

C'est mon chemin:

internal static class ResourceAccessor
{
    public static Uri Get(string resourcePath)
    {
        var uri = string.Format(
            "pack://application:,,,/{0};component/{1}"
            , Assembly.GetExecutingAssembly().GetName().Name
            , resourcePath
        );

        return new Uri(uri);
    }
}

Usage:

new BitmapImage(ResourceAccessor.Get("Images/1.png"))

8

Voici un exemple qui définit dynamiquement le chemin de l'image (image située quelque part sur le disque plutôt que de créer en tant que ressource):

if (File.Exists(imagePath))
{
    // Create image element to set as icon on the menu element
    Image icon = new Image();
    BitmapImage bmImage = new BitmapImage();
    bmImage.BeginInit();
    bmImage.UriSource = new Uri(imagePath, UriKind.Absolute);
    bmImage.EndInit();
    icon.Source = bmImage;
    icon.MaxWidth = 25;
    item.Icon = icon;
}

Réflexions sur les icônes ...

À première vue, vous penseriez que la propriété Icon ne peut contenir qu'une image. Mais il peut en fait contenir n'importe quoi! J'ai découvert cela par accident lorsque j'ai essayé par programme de définir la Imagepropriété directement sur une chaîne avec le chemin d'accès à une image. Le résultat était qu'il ne montrait pas l'image, mais le texte réel du chemin!

Cela conduit à une alternative pour ne pas avoir à créer une image pour l'icône, mais utiliser du texte avec une police de symbole à la place pour afficher une simple "icône". L'exemple suivant utilise la police Wingdings qui contient un symbole "floppydisk". Ce symbole est vraiment le caractère <, qui a une signification particulière en XAML, nous devons donc utiliser la version encodée à la &lt;place. Cela fonctionne comme un rêve! Voici un symbole de disquette sous forme d'icône sur l'élément de menu:

<MenuItem Name="mnuFileSave" Header="Save" Command="ApplicationCommands.Save">
  <MenuItem.Icon>
    <Label VerticalAlignment="Center" HorizontalAlignment="Center" FontFamily="Wingdings">&lt;</Label>
  </MenuItem.Icon>
</MenuItem>

Question: " L'image est incorporée en tant que ressource dans le projet ", réponse: " Voici un exemple [pour une image] situé quelque part sur le disque plutôt que de créer en tant que ressource ".
mn

7

Si votre image est stockée dans un ResourceDictionary, vous pouvez le faire avec une seule ligne de code:

MyImage.Source = MyImage.FindResource("MyImageKeyDictionary") as ImageSource;

6

Il existe également un moyen plus simple. Si l'image est chargée en tant que ressource dans le XAML et que le code en question est le code-behind pour ce contenu XAML:

Uri iconUri = new Uri("pack://application:,,,/ImageNAme.ico", UriKind.RelativeOrAbsolute);
NotifyIcon.Icon = BitmapFrame.Create(iconUri);

4

Mettez le cadre dans un VisualBrush:

VisualBrush brush = new VisualBrush { TileMode = TileMode.None };

brush.Visual = frame;

brush.AlignmentX = AlignmentX.Center;
brush.AlignmentY = AlignmentY.Center;
brush.Stretch = Stretch.Uniform;

Mettez le VisualBrush dans GeometryDrawing

GeometryDrawing drawing = new GeometryDrawing();

drawing.Brush = brush;

// Brush this in 1, 1 ratio
RectangleGeometry rect = new RectangleGeometry { Rect = new Rect(0, 0, 1, 1) };
drawing.Geometry = rect;

Maintenant, placez le GeometryDrawing dans une DrawingImage:

new DrawingImage(drawing);

Placez-le sur votre source d'image, et voilà!

Vous pourriez cependant le faire beaucoup plus facilement:

<Image>
    <Image.Source>
        <BitmapImage UriSource="/yourassembly;component/YourImage.PNG"></BitmapImage>
    </Image.Source>
</Image>

Et en code:

BitmapImage image = new BitmapImage { UriSource="/yourassembly;component/YourImage.PNG" };

LOL! Pourquoi le rendre facile quand vous pouvez le rendre difficile pour la première fois :) Je vais essayer votre solution simple avant d'accepter ...
Torbjørn

En fait, cela ne m'a pas aidé du tout. Peut-être que je suis stupide :( Je n'ai pas le temps de regarder de plus près en ce moment (projet pour animaux de compagnie). Je voudrais plus de réponses quand j'y reviendrai :)
Torbjørn

3

Il existe également un moyen plus simple. Si l'image est chargée en tant que ressource dans le XAML et que le code en question est le code derrière pour ce XAML:

Voici le dictionnaire de ressources pour un fichier XAML - la seule ligne qui vous intéresse est l'ImageBrush avec la clé "PosterBrush" - le reste du code est juste pour montrer le contexte

<UserControl.Resources>
        <ResourceDictionary>
            <ImageBrush x:Key="PosterBrush" ImageSource="..\Resources\Images\EmptyPoster.jpg" Stretch="UniformToFill"/>

        </ResourceDictionary>
    </UserControl.Resources>

Maintenant, dans le code derrière, vous pouvez simplement le faire

ImageBrush posterBrush = (ImageBrush)Resources["PosterBrush"];

2

Comment charger une image depuis des icônes de ressources et des images (version corrigée d'Arcturus):

Supposons que vous souhaitiez ajouter un bouton avec une image. Que devrais tu faire?

  1. Ajouter aux icônes du dossier de projet et mettre l'image ClickMe.png ici
  2. Dans les propriétés de 'ClickMe.png', définissez 'BuildAction' sur 'Resource'
  3. Supposons que le nom de votre assemblage compilé soit «Company.ProductAssembly.dll».
  4. Il est maintenant temps de charger notre image en XAML

    <Button Width="200" Height="70">
      <Button.Content>
        <StackPanel>
          <Image Width="20" Height="20">
            <Image.Source>
              <BitmapImage UriSource="/Company.ProductAssembly;component/Icons/ClickMe.png"></BitmapImage>
              </Image.Source>
          </Image>
          <TextBlock HorizontalAlignment="Center">Click me!</TextBlock>
        </StackPanel>
      </Button.Content>
    </Button>

Terminé.


2

Je suis nouveau sur WPF, mais pas en .NET.

J'ai passé cinq heures à essayer d'ajouter un fichier PNG à un «projet de bibliothèque de contrôles personnalisés WPF» dans .NET 3.5 (Visual Studio 2010) et à le définir comme arrière-plan d'un contrôle hérité d'une image.

Rien de relatif avec les URI n'a fonctionné. Je ne peux pas imaginer pourquoi il n'y a pas de méthode pour obtenir un URI à partir d'un fichier de ressources, via IntelliSense, peut-être comme:

Properties.Resources.ResourceManager.GetURI("my_image");

J'ai essayé beaucoup d'URI et joué avec ResourceManager et les méthodes GetManifest de l'assembly, mais il y avait toutes des exceptions ou des valeurs NULL.

Ici, je mets le code qui a fonctionné pour moi:

// Convert the image in resources to a Stream
Stream ms = new MemoryStream()
Properties.Resources.MyImage.Save(ms, ImageFormat.Png);

// Create a BitmapImage with the stream.
BitmapImage bitmap = new BitmapImage();
bitmap.BeginInit();
bitmap.StreamSource = ms;
bitmap.EndInit();

// Set as source
Source = bitmap;

3
Je pense que le problème est que WPF n'aime pas l'approche traditionnelle consistant à placer des ressources dans des fichiers resx. Au lieu de cela, vous êtes censé ajouter l'image directement à votre projet et définir sa propriété Build Action sur Resource. Je ne sais pas quelle est la différence (en terme de format de fichier physique) entre les deux approches.
Qwertie

1
Assurez-vous que l'action de construction est un contenu
Jerry Nixon

1

Si vous avez déjà un flux et connaissez le format, vous pouvez utiliser quelque chose comme ceci:

static ImageSource PngStreamToImageSource (Stream pngStream) {
    var decoder = new PngBitmapDecoder(pngStream,
        BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.Default);
    return decoder.Frames[0];
}

1

Vous venez de manquer un peu.

Pour obtenir une ressource intégrée à partir de n'importe quel assembly, vous devez mentionner le nom de l'assembly avec votre nom de fichier comme je l'ai mentionné ici:

Assembly asm = Assembly.GetExecutingAssembly();
Stream iconStream = asm.GetManifestResourceStream(asm.GetName().Name + "." + "Desert.jpg");
BitmapImage bitmap = new BitmapImage();
bitmap.BeginInit();
bitmap.StreamSource = iconStream;
bitmap.EndInit();
image1.Source = bitmap;

BeginInit () ne semble pas exister dans WPF.
Myosotis

Il est disponible sous le lien ci-dessous msdn.microsoft.com/en-us/library/…
maulik kansara
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.