get-the-solution

MarkupExtension – Bilder aus Resource direkt ins XAML laden

By
on Regards: .NET Framework; C#; XAML;

Vor kurzem habe ich eine MarkupExtension geschrieben, die aus einer Resource-Datei (resx.) Bilder lädt. Eine MarkupExtension zu schreiben ist nicht schwer. Man erbt von der Abstrakten Klasse MarkupExtension und muss die zwei Konstruktoren (einmal mit Parameter) implementieren und die Methode ProvideValue überschreiben.

1.0 Einführung - Eine einfache MarkupExtension

Im zweiten Konstruktor der wie folgt aussieht....

public ImageResourceConverterExtension(string pkey) : this()
{ 
  //Wert Propertie Key zuweisen
  Key = pkey;
}

....steht im Parameter der MarkupExtension Key.

D.h. Wenn im XAML in der MarkupExtension STRING

<TextBlock Text="{GetCommon:MeineMarkupExtension STRING}"/>

steht, erhält man diesen Wert "STRING" über den Konstruktor Parameter pkey. Diesen kann man dann, interpretieren parsen und vieles mehr :).

In unserem Beispiel geben wir den Key einfach wieder als string an die Propertie zurück an der die MarkupExtension angewandt wurde.

Die Funktion ProvideValue weist ein object der Propertie zu, an der die MarkupExtension gesetzt wurde. Z.B

public override object ProvideValue(IServiceProvider pserviceProvider)
{
  //Propertie in der wir unseren Key "STRING" gespeichert haben
  return Key;
}

Das heißt im TextBlock Text steht dann, wenn das Fenster geladen wurde, STRING.

1.1 ImageResourceConverter MarkupExtension

So und jetzt meine MarkupExtension die Bilder aus einer Resource.resx lädt.

Achtung: die MarkupExtension funktioniert nur mit PNG Bilder!

Verwendung wie folgt:

<Image Source="{GetCommon:ImageResourceConverter Get.Common.Resource.Image1}"/>

Syntax: Namespace kann auch der ProjektName sein.ResourceName.Name des Bildes auf die man zugreifen will. Gibt man hinter dem Namespace.ResourceName.BildName kein @ an nimmt die MarkupExtension an, dass die Resource sich in der aktuellen Instanz befindet.

<Image Source="{GetCommon:ImageResourceConverter Get.Demo.Resource.Image1@Get.Demo.exe}" Width="20"/>

Syntax: Namespace.ResourceName.BildName@DLLName in der sich die Resource befindet Die Dll wird im Verzeichnis Environment.CurrentDirectory gesucht.

Hier der Code der MarkupExtension:

    [MarkupExtensionReturnType(typeof(ImageSource)), Localizability(LocalizationCategory.NeverLocalize)]
    public class ImageResourceConverterExtension : MarkupExtension
    {
        #region Members
        /// <summary>
        /// Seperator zwischen Namespace und Resource
        /// </summary>
        private const char _point = '.';

        /// <summary>
        /// Seperator zwischen ProjektName.ResourceName.Name und DLLName
        /// </summary>
        private const char _ExternAssemblySpliter = '@';
        #endregion

        #region Konstruktor
        /// <summary>
        /// Konstruktor - Initialisiert die Properties
        /// </summary>
        public ImageResourceConverterExtension()
        {
            ImageName = string.Empty;
            BaseName = string.Empty;
        }
        /// <summary>
        /// Konstruktor
        /// </summary>
        /// <param name="pkey">Key der bei der MarkupExtension angegeben wurde.
        ///  Z.B. "Get.Demo.Resource.Image1@Get.Demo.exe"</param>
        public ImageResourceConverterExtension(string pkey)
            : this()
        {
            Key = pkey;

            //Prüfen ob die Resource-Datei aus einer fremden Assembly geladen werden soll
            if (!pkey.Contains(_ExternAssemblySpliter))
            {
                //Properties ImageName und BaseName setzen
                SetImageNameAndBaseName(pkey);
            }
            else
            {
                //Fremde Assembly laden - Dateiname extrahieren
                string filename = pkey.Split(_ExternAssemblySpliter).Last();
                FileInfo fileInfo = new FileInfo(Environment.CurrentDirectory + "" + filename);

                if (!fileInfo.Exists)
                    throw new FileNotFoundException();

                //Assembly laden
                Assembly assembly = Assembly.LoadFrom(fileInfo.ToString());
                ResourceAssembly = assembly;

                SetImageNameAndBaseName(pkey.Replace(_ExternAssemblySpliter.ToString() 
                + filename, string.Empty));
            }

        }
        #endregion

        #region Funktionen
        /// <summary>
        /// Extrahiert die Werte aus dem übergeben Key und weist sie den jeweiligen Properties zu.
        /// </summary>
        /// <param name="pkey">MarkupExtension Key Z.B. "Get.Demo.Resource.Image1@Get.Demo.exe"</param>
        private void SetImageNameAndBaseName(string pkey)
        {
            //Letzter Value ist der BildName
            ImageName = pkey.Split(_point).Last();
            //Der Rest besteht aus Namespace.ResourceName
            BaseName = pkey.Replace(_point.ToString() + ImageName, string.Empty);
        }
        /// <summary>
        /// Gibt ein ImageSource Objekt zurück an der die MarkupExtension gesetzt wurde.
        /// </summary>
        /// <param name="pserviceProvider">Objekt, das Dienste für die 
        /// Markuperweiterung bereitstellen kann.</param>
        /// <returns>ImageSource Objekt</returns>
        public override object ProvideValue(IServiceProvider pserviceProvider)
        {
            //Bild aus Resource laden
            if (ResourceAssembly == null)
                return GetBitmapImageFromResource(BaseName, ImageName, this.GetType().Assembly);
            else
                return GetBitmapImageFromResource(BaseName, ImageName, ResourceAssembly);
        }
        /// <summary>
        /// Ladet mithilfe der übergebenen Informationen ein Bild aus der
        /// Resource (.resx) und konvertiert es in ein ImageSource Objekt
        /// </summary>
        /// <param name="pBaseName"></param>
        /// <param name="pImageName"></param>
        /// <param name="pAssembly"></param>
        /// <returns></returns>
        public static ImageSource GetBitmapImageFromResource(string pBaseName, 
        string pImageName, Assembly pAssembly)
        {
            ResourceManager resourceManager = new ResourceManager(pBaseName, pAssembly);

            //System.Drawing.Bitmap aus Resource holen
            object image = resourceManager.GetObject(pImageName);
            if (image == null) return null;

            if (!image.GetType().Equals(typeof(Bitmap))) return null;

            Bitmap bitmap = image as Bitmap;

            //Bitmap in ImageSource konvertieren
            BitmapImage bitmapImage = new BitmapImage();
            MemoryStream stream = new MemoryStream();

            bitmap.Save(stream, ImageFormat.Png);

            //ImageSource aus Stream holen
            //http://cornucopia30.blogspot.com/2007/08/wpf-point-image-to-embedded-resource.html
            PngBitmapDecoder bitmapDecoder = new PngBitmapDecoder(stream, 
            BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.Default);
            ImageSource imageSource = bitmapDecoder.Frames[0];
            return imageSource;
        }
        #endregion Funktionen

        #region Properties

        /// <summary>
        /// Gets or sets the resource key.
        /// </summary>
        /// <value>The key.</value>
        [ConstructorArgument("pkey")]
        public string Key { get; private set; }

        /// <summary>
        /// Ruft den Namespace bzw. Projekt-Namen + Resourcename ab oder legt diesen fest.
        /// </summary>
        public string BaseName { get; private set; }

        /// <summary>
        /// Ruft den Wert des Bildnamens ab oder legt diesen fest.
        /// </summary>
        public string ImageName { get; private set; }

        /// <summary>
        /// Ruft die Externe Assembly ab oder legt diese fest.
        /// Diese Propertie ist nur gesetzt wenn man in der MarkupExtension 
        /// das @ mit dem DLLNamen festgelegt hat.
        /// </summary>
        public Assembly ResourceAssembly { get; private set; }

        #endregion
    }

Hier eine kurze Beschreibung der MarkupExtension:

Im Key wird angegeben wo sich das Bild in der Resource befindet. Die Resource wird in der Funktion GetBitmapImageFromResource mit dem ResourceManager geladen.

Die Propertie Source der Image Klasse hat den Typ ImageSource. Deshalb muss man das geladene Bitmap in eine ImageSource umwandeln.

Anschließend wird die erzeugte ImageSource in der Funktion ProvideValue zurück gegeben. Diese MarkupExtension habe ich im schnell verfahren geschrieben. Das heißt, dass wichtige Überprüfungen fehlen. Auch habe ich das Ganze nur für .PNG Dateien getestet bzw. Programmiert. Ich überprüfe auch nicht ob man die MarkupExtension bei einem Nicht Image Objekt anwendet.

Download Projekt