IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Windows Presentation Foundation Discussion :

Utiliser en XAML des resources de l'application [FAQ]


Sujet :

Windows Presentation Foundation

  1. #1
    Rédacteur/Modérateur


    Homme Profil pro
    Développeur .NET
    Inscrit en
    Février 2004
    Messages
    19 875
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Développeur .NET
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Février 2004
    Messages : 19 875
    Par défaut Utiliser en XAML des resources de l'application
    Salut,

    Je fais une appli WPF, donc avec des fenêtres en XAML. J'aimerais savoir s'il est possible d'utiliser dans le code XAML des resources de l'application, déclarées dans Resources.resx. Quelque chose comme ça :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    ...
        <Image Source="{StaticResource monImage}"/>
    ...
    Malheureusement il semblerait que ce ne soit pas prévu... logique d'ailleurs, les resources déclarées dans le .resx sont des System.Drawing.Bitmap, et WPF utilise des System.Windows.Media.ImageSource. J'ai trouvé un article qui explique comment faire la conversion entre les deux, j'ai donc créé une méthode qui obtient un objet ImageSource à partir de l'identifiant de l'image dans la resource :
    Code c# : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
     
        public class ResourceHelper
        {
            public static ImageSource GetImageSource(string resourceKey)
            {
                Bitmap bmp = Properties.Resources.ResourceManager.GetObject(resourceKey) as Bitmap;
                Int32Rect rect = new Int32Rect(0, 0, bmp.Width, bmp.Height);
                BitmapSource src = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(bmp.GetHbitmap(), IntPtr.Zero, rect, BitmapSizeOptions.FromEmptyOptions());
                return src;
            }
        }
    Seulement voilà, j'aimerais bien ne pas être obligé de passer par le code C# pour indiquer la propriété Source d'une image XAML, c'est quand même un peu contraire au concept de séparation du design et du code... Je sais qu'il est possible de faire un Binding vers une méthode en utilisant un ObjectDataSource, mais j'ai pas trop compris comment ça marche. J'aimerais bien pouvoir faire quelque chose dans ce gout là :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    ...
        <Image Source="{x:JeSaisPasQuoi ResourceHelper.GetImageSource('monImage')"/>
    ...
    J'ai cherché sur , mais j'ai rien trouvé... Est-ce que vous auriez une idée ?

  2. #2
    Rédacteur/Modérateur


    Homme Profil pro
    Développeur .NET
    Inscrit en
    Février 2004
    Messages
    19 875
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Développeur .NET
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Février 2004
    Messages : 19 875
    Par défaut
    Yes ! J'ai trouvé une solution que je trouve assez élégante, j'en suis très fier ... il faudra peut-être l'améliorer pour que ce soit vraiment réutilisable, mais pour ce que je veux en faire dans l'immédiat ça ira très bien.
    Je vous donne ma solution, ça pourra éventuellement servir à quelqu'un !
    En fait, on peut créer ses propres "markup extensions" en héritant de la classe MarkupExtension. J'avais peur que ce soit super compliqué, mais en fait c'est assez simple, ça a marché du premier coup
    Voilà le code :
    Code c# : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
     
    using System;
    using System.Windows.Markup;
    using System.Drawing;
    using System.Windows;
    using System.Windows.Media;
    using System.Windows.Media.Imaging;
     
    namespace MonAppli
    {
        [MarkupExtensionReturnType(typeof(ImageSource))]
        public class ImageResourceExtension : MarkupExtension
        {
            [ConstructorArgument("resourceKey")]
            public string ResourceKey { get; set; }
     
            public ImageResourceExtension(string resourceKey)
            {
                ResourceKey = resourceKey;
            }
     
            public override object ProvideValue(IServiceProvider serviceProvider)
            {
                return GetImageSource(ResourceKey);
            }
     
            public static ImageSource GetImageSource(string resourceKey)
            {
                Bitmap bmp = Properties.Resources.ResourceManager.GetObject(resourceKey) as Bitmap;
                Int32Rect rect = new Int32Rect(0, 0, bmp.Width, bmp.Height);
                BitmapSource src = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(bmp.GetHbitmap(), IntPtr.Zero, rect, BitmapSizeOptions.FromEmptyOptions());
                return src;
            }
        }
    }

    Ensuite dans le code XAML, il suffit de faire comme ça (en supposant que Resources.resx contienne une image appelée "monImage") :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    <Image Source="{local:ImageResource monImage}"/>
    (il faut avoir déclaré le namespace local dans la racine du document :
    xmlns:local="clr-namespace:MonAppli")

    C'est simple, c'est efficace, et cerise sur le gateau, ça affiche même l'image dans le designer graphique

    Je me demande d'ailleurs pourquoi Microsoft n'a pas prévu un truc similaire... c'est quand même bête, alors qu'on a une interface pratique pour ajouter des ressources à une application (héritée de VS2005), de pas pouvoir utiliser ces ressources avec WPF !

  3. #3
    Rédacteur
    Avatar de Thomas Lebrun
    Profil pro
    Inscrit en
    Octobre 2002
    Messages
    9 161
    Détails du profil
    Informations personnelles :
    Âge : 43
    Localisation : France

    Informations forums :
    Inscription : Octobre 2002
    Messages : 9 161
    Par défaut
    Pas mal du tout

    On va le mettre dans la FAQ



  4. #4
    Rédacteur/Modérateur


    Homme Profil pro
    Développeur .NET
    Inscrit en
    Février 2004
    Messages
    19 875
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Développeur .NET
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Février 2004
    Messages : 19 875
    Par défaut
    Je tiens quand même à signaler une limitation importante :
    Vu la référence directe à Properties.Resources, ça ne fonctionne que si on déclare ImageResourceExtension dans le même assembly que les ressources... donc dans l'état actuel, on ne peut pas le mettre dans une librairie réutilisable par un autre projet. Mais je ne pense pas que ce soit très difficile de l'adapter, en passant d'autres paramètres pour dire où se trouvent les resources.

  5. #5
    Rédacteur/Modérateur


    Homme Profil pro
    Développeur .NET
    Inscrit en
    Février 2004
    Messages
    19 875
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Développeur .NET
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Février 2004
    Messages : 19 875
    Par défaut
    Voilà ma nouvelle version (j'ai aussi renommé la classe). J'ai hérité de StaticExtension, en overridant juste ProvideValue pour convertir l'objet renvoyé par la classe de base.
    Cette version devrait pouvoir s'utiliser à partir d'un autre assembly (une librairie de classes par exemple) puisqu'on précise où est l'image exactement, au lieu de supposer qu'elle est dans le Properties.Resources de l'assembly courant.

    Code c# : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
     
    using System;
    using System.Windows.Markup;
    using System.Drawing;
    using System.Windows;
    using System.Windows.Media.Imaging;
     
    namespace MyLibrary
    {
        [MarkupExtensionReturnType(typeof(BitmapSource))]
        public class StaticBitmapExtension : StaticExtension
        {
            public StaticBitmapExtension(string member)
                : base(member)
            {
     
            }
     
            public override object ProvideValue(IServiceProvider serviceProvider)
            {
                return GetImageSource(base.ProvideValue(serviceProvider));
            }
     
            public static BitmapSource GetImageSource(object obj)
            {
                if (obj is Bitmap)
                {
                    Bitmap bmp = obj as Bitmap;
                    Int32Rect rect = new Int32Rect(0, 0, bmp.Width, bmp.Height);
                    BitmapSource src = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(bmp.GetHbitmap(), IntPtr.Zero, rect, BitmapSizeOptions.FromEmptyOptions());
                    return src;
                }
                else
                {
                    throw new Exception("Only Bitmap images are supported");
                }
            }
        }
    }

    Pour l'utiliser c'est simple (en supposant qu'on ait dans les ressources une image "toto") :

    Code xml : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
     
    <Window x:Class="WpfApplication1.Window1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:prop="clr-namespace:WpfApplication1.Properties"
        xmlns:lib="clr-namespace:MyLibrary"
        Title="Window1" Height="300" Width="300">
        <Grid>
            <Image Source="{lib:StaticBitmap prop:Resources.toto}" />
        </Grid>
    </Window>

    Merci à Jerem22 pour son post qui m'a donné l'idée de faire comme ça...

  6. #6
    Membre confirmé
    Profil pro
    Inscrit en
    Décembre 2004
    Messages
    134
    Détails du profil
    Informations personnelles :
    Âge : 41
    Localisation : France

    Informations forums :
    Inscription : Décembre 2004
    Messages : 134
    Par défaut
    salut,

    ton code c'est uniquement pour utiliser des images qui ont été ajoutées dans les ressources du projet ?
    Ou bien est-ce que je peux faire la même chose avec des fichiers xaml qui ont eux aussi été ajoutés dans les ressources.



    Je m'explique, avec adobe Illustrator, j'ai fait un dessin, que j'ai exporté en xaml.

    le fichier xaml généré ressemble a ça :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    <!-- Generated by Adobe Illustrator CS -> XAML Export Plug-In Version 0.17      -->
    <!-- For questions, contact Mike Swanson: http://www.mikeswanson.com/XAMLExport -->
     
    <Viewbox Width="108.288086" Height="113.723633"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <Canvas Width="108.288086" Height="113.723633">
     
     ...
     
    </Canvas>
    </Viewbox>

    j'ai ajouté ensuite ce fichier xaml dans mes ressources. Car il s'agit simplement d'un fichier design, ya pas de code c# derrière.

    j'ai définit d'autres controles WPF perso en xaml+c#, et j'aimerais qui charge ce fichier xaml afin d'avoir son aspect son aspect.

    Comment je doit faire ?

  7. #7
    Rédacteur/Modérateur


    Homme Profil pro
    Développeur .NET
    Inscrit en
    Février 2004
    Messages
    19 875
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Développeur .NET
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Février 2004
    Messages : 19 875
    Par défaut
    C'est seulement pour les images... l'intérêt de cette extension est de pouvoir utiliser les images des ressources (de type Bitmap) dans le XAML (qui attend des ImageSource). L'extension prend en paramètre un Bitmap, et renvoie une ImageSource.
    Mais si tu veux récupérer un objet quelconque des ressources (sans conversion) tu peux faire simplement :
    (exemple avec une chaîne)
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    <object property="{x:Static my:AType.AStaticMember}"/>
    (où my est le mapping xml du namespace qui contient AType)

  8. #8
    Membre averti
    Homme Profil pro
    Développeur .NET
    Inscrit en
    Octobre 2006
    Messages
    12
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 45
    Localisation : France, Pas de Calais (Nord Pas de Calais)

    Informations professionnelles :
    Activité : Développeur .NET

    Informations forums :
    Inscription : Octobre 2006
    Messages : 12
    Par défaut
    Citation Envoyé par tomlev Voir le message
    Je tiens quand même à signaler une limitation importante :
    Vu la référence directe à Properties.Resources, ça ne fonctionne que si on déclare ImageResourceExtension dans le même assembly que les ressources... donc dans l'état actuel, on ne peut pas le mettre dans une librairie réutilisable par un autre projet. Mais je ne pense pas que ce soit très difficile de l'adapter, en passant d'autres paramètres pour dire où se trouvent les resources.

    Bonjour,

    J'ai traité un problème d'externalisation de ressources et j'ai trouvé 2 façon de faire :
    1) ajouter "[assembly: InternalsVisibleTo("AssemblyName")] " dans l'assembly info.cs du projet contenant les ressources, pour donner accès aux membres internes aux assemblies déclarées en paramètre
    2) Ecrire un petit exe (ou trouver un petit programme de confiance déjà implémenté) qui se substitue à ResXFileCodeGenerator (propriété du resx "Outil personnalisé") et qui rend public tous les membres ressources.

    Si ça peut t'aider...

  9. #9
    Rédacteur/Modérateur


    Homme Profil pro
    Développeur .NET
    Inscrit en
    Février 2004
    Messages
    19 875
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Développeur .NET
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Février 2004
    Messages : 19 875
    Par défaut
    oui mais si tu regardes un peu plus bas, la limitation n'existe plus... j'ai fait une nouvelle version de l'extension (juste avant le post d'Ikit)

  10. #10
    Membre habitué
    Inscrit en
    Mai 2008
    Messages
    13
    Détails du profil
    Informations forums :
    Inscription : Mai 2008
    Messages : 13
    Par défaut
    J'espère ne pas être à côté de la plaque pour un premier message mais .. hmm .. y'aurai pas beaucoup plus simple ?

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    <Image Source="/Resources/TestIcon.png"/>
    Si le fichier TestIcon.jpg est un fichier ajouté aux ressources dans les ressources.
    nan ?

    Je vous laisse, j'étais venu chercher un code pour convertir une ressource Bitmap en ImageSource et je l'ai maintenant grâce à vous.

    D'ailleurs, d'après mes expérimentations, il n'est pas nécessaire de retrouver la taille de l'image d'où la simplifcation :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    ImageSource imageSource = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(Properties.Resources.ErrorIcon.GetHbitmap(),
    IntPtr.Zero, Int32Rect.Empty,
    System.Windows.Media.Imaging.BitmapSizeOptions.FromEmptyOptions());
    R.

  11. #11
    Rédacteur/Modérateur


    Homme Profil pro
    Développeur .NET
    Inscrit en
    Février 2004
    Messages
    19 875
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Développeur .NET
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Février 2004
    Messages : 19 875
    Par défaut
    Citation Envoyé par Raphael77222 Voir le message
    J'espère ne pas être à côté de la plaque pour un premier message mais .. hmm .. y'aurai pas beaucoup plus simple ?

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    <Image Source="/Resources/TestIcon.png"/>
    Si le fichier TestIcon.jpg est un fichier ajouté aux ressources dans les ressources.
    nan ?
    Oui, bien sûr, c'est la façon "naturelle" de faire. Mais si tu veux utiliser des ressources intégrées (dans un fichier .resx), par exemple parce que tu migres à partir d'une appli Windows Forms existante, tu ne peux pas faire comme ça.

  12. #12
    Membre averti
    Profil pro
    Inscrit en
    Décembre 2006
    Messages
    38
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2006
    Messages : 38
    Par défaut
    Citation Envoyé par tomlev Voir le message
    Oui, bien sûr, c'est la façon "naturelle" de faire. Mais si tu veux utiliser des ressources intégrées (dans un fichier .resx), par exemple parce que tu migres à partir d'une appli Windows Forms existante, tu ne peux pas faire comme ça.
    La question serait alors comment ?
    Vu qu'il ne s'agit que de récuperer un fichier dans un .resx et de le mettre dans un xaml sans faire moult codes en plus (ni toucher au .cs)

    question donc comment à partir du .resx dans lequel il ya une image, la mettre dans un Xaml sans se prendre la tête ?

    Disons que le fichier resx se trouved ans le repertoire Outils et s'appelle RessourcesImages et l'image Img1.png par exemple.

  13. #13
    Rédacteur/Modérateur


    Homme Profil pro
    Développeur .NET
    Inscrit en
    Février 2004
    Messages
    19 875
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Développeur .NET
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Février 2004
    Messages : 19 875
    Par défaut
    la réponse est dans le post n°5
    il suffit que ton fichier resx soit inclus dans ton projet en tant que ressource incorporée

  14. #14
    Membre averti
    Profil pro
    Inscrit en
    Mai 2006
    Messages
    39
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2006
    Messages : 39
    Par défaut
    Bonjour à tous,

    Alors déjà je viens féliciter TomLev, car ta classe est extrêmement pratique ^^

    Bon alors en fait je l'utilise à la fois dans mon code behind et mon code XAML. Néanmoins en XAML j'ai quelques soucis en effet, j'ai comme erreur

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    Error	1	'prop' is an undeclared namespace.  Error at Line 1 Position 400.
    J'ai déclaré dans les namespaces

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
        xmlns:resImg="clr-namespace:UIL_WPF" //Contient la classe de TomLev avec le namespace de mon application (UIL_WPF)
        xmlns:prop="clr-namespace:UIL_WPF.Properties"
    Je fais appel à ces namespace dans une ListBox, pour une image !
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    <Image Height="16" Width="16"  Source="{resImg:StaticBitmap prop:Resources.fleche}"></Image>

    J'ai déclaré les ressources en "Embedded Resources", et je ne copie pas les fichiers à la sortie.

    Donc l'application marche correctement mais avec l'erreur précité ci-dessus, ce qui est un peu génant, car maintenant je ne vois plus mon interface "Design"

  15. #15
    Rédacteur/Modérateur


    Homme Profil pro
    Développeur .NET
    Inscrit en
    Février 2004
    Messages
    19 875
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Développeur .NET
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Février 2004
    Messages : 19 875
    Par défaut
    Dans le code C#, tu accèdes bien à ton image via UIL_WPF.Properties.Resources.fleche ?

  16. #16
    Membre averti
    Profil pro
    Inscrit en
    Mai 2006
    Messages
    39
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2006
    Messages : 39
    Par défaut
    Ah oui j'y accède bien, et le plus bizarre, c'est quand j'exécute ma fenêtre, l'image "fleche" est bien présente !!

    Un bon cas pratique en fait !! Ça marche, mais je ne sais pas pourquoi !

  17. #17
    Membre éprouvé Avatar de anthyme
    Homme Profil pro
    Inscrit en
    Mars 2004
    Messages
    1 559
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Mars 2004
    Messages : 1 559
    Par défaut
    Citation Envoyé par tomlev Voir le message
    Je me demande d'ailleurs pourquoi Microsoft n'a pas prévu un truc similaire... c'est quand même bête, alors qu'on a une interface pratique pour ajouter des ressources à une application (héritée de VS2005), de pas pouvoir utiliser ces ressources avec WPF !
    pour la même raison qu'il n y a pas de datepicker, datagrid, designer du niveau de winform...

    Le xaml/WPF c'est super flexible, de la vrai pate a modelé mais ca manque de composant haut niveau...

    Comme d hab faut attendre la v2 pour avoir un truc complet (comme la majorité des produit MS)

    c un peu comme le tic/toc d'intel

    jolie code sinon je m en servirai ptetre un jour.

Discussions similaires

  1. Réponses: 6
    Dernier message: 09/02/2011, 10h15
  2. Réponses: 1
    Dernier message: 14/12/2009, 09h47
  3. comment proteger une c# application contre les editeur des resources
    Par boulmaouahibe dans le forum Windows Forms
    Réponses: 2
    Dernier message: 18/03/2009, 00h54
  4. Création et utilisation des Plugins pour les applications
    Par tayeb.meftah dans le forum Langage
    Réponses: 3
    Dernier message: 20/02/2009, 18h03

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo