get-the-solution


Page 2 of 3123

C# Anwendung Plugin fähig machen

By
on Regards: .NET Framework; C#;

In diesem Beispiel sieht man wie man ein einfaches Plugin-System realisiert. Demonstriert wird das ganze anhand eines „Mathematikprogramms“.
Das Mathematikprogramm lädt zu Beginn alle Plugins. Wenn der Benutzer z.B. die Wurzelfunktion aufruft mit „sqrt(3)“ durchsucht das Mathematikprogramm alle Plugins und schaut nach ob eines der Plugins die Funktion „sqrt“ berechnen kann. Der Pluginname verrät, für welche Mathematikfunktion das Plugin zu ständig ist. Wurde kein zuständiges Plugin gefunden, wird die Meldung „unbekannte Funktion“ ausgegeben.
Gibt der Benutzer als Eingabe z.B. „sqrt(4)“ ein, wird dem Plugin, das die Funktion „sqrt“ implementiert, „sqrt(4)“ übergeben. Somit weiß das Plugin was es überhaupt berechnen soll. Zurück gibt es das berechnete Ergebnis  der Eingabe.
Damit wir wissen wie die Plugins „aussehen“, also wie die Funktionen, Eigenschaften heißen, müssen wir ein Interface für die Plugins definieren. Ansonsten könnte es passieren (wenn verschiedene Entwickler die Plugins erstellen) dass die zu berechnende Funktion einmal „Berechne(String input)“ oder „Calculate(int input)“ heißt.
Wenn jeder das Interface des Plugins implementiert, kann es nicht zu solchen Problemen kommen. Unser Plugin-Interface sieht wie folgt aus.
            /// <summary>
	    /// Definiert das "Aussehen" unseres Plugins
	    /// </summary>
	    public interface IPlugin
	    {
	        /// <summary>
	        /// Name des Plugins
	        /// </summary>
	        String Name { get; set; }
	 
	        /// <summary>
	        /// Beschreibung des Plugins
	        /// </summary>
	        String Description { get; set; }
	 
	        /// <summary>
	        /// Berechnet die Eingabe
	        /// </summary>
	        /// <param name="pInput">Eingabe die ausgewertet werden soll.</param>
	        /// <returns>Gibt das berechnete Ergebnis zurück</returns>
	        String Calc(String pInput);
	    }

Das bedeutet, dass jeder, der unser Interface implementiert zwei Properties “Name” und “Description” definiert und zu guter Letzt eine Funktion “Calc”.

Im Namen speichern wir den Funktionsnamen der Mathematischen Funktion. Als z.B. „sqrt“. In der Funktion Calc berechnen wir den Übergabewert pInput und geben das Ergebnis als String zurück. Natürlich wäre eine andere Mathematische Funktion wie z.B. Modulointeressanter zu implementieren.Als nächstes gilt es die Mathematik Klasse MathCore zu erstellen, welche die Plugins lädt und erstellt.

Als erstes wird die Funktion LoadPlugins aufgerufen. Dieser übergeben wir den Dateipfad in dem sich die Plugins befinden.

Als nächstes gilt es die Mathematik Klasse MathCore zu erstellen, welche die Plugins lädt und erstellt.
Als erstes wird die Funktion LoadPlugins aufgerufen. Dieser übergeben wir den Dateipfad in dem sich die Plugins befinden.

        public void LoadPlugins(String pPluginPath)
	{
	            String[] files = Directory.GetFiles(pPluginPath);
	 
	            foreach (String file in files)
	            {
	                FileInfo fileInfo = new FileInfo(file);
	 
	                if (fileInfo.Extension.Equals(".dll"))
	                {
	                    Dictionary<string, object> dictionary = GetModul(file, typeof(IPlugin.IPlugin));
	                    foreach (var a in dictionary)
	                    {
	                        Plugins.Add(a.Key,a.Value);
	                    }
	 
	                }
	 
	            }
	}

Es werden alle Dateien aus dem Verzeichnis gelesen und Dateien mit der Dateiendung „.dll“ gefiltert. Ist eine Datei eine „.dll“, wird die Funktion GetModul(file, Type) aufgerufen. Diese lädt die Plugins in unsere Instanz hinein. Sehen wir uns dazu diese Funktion genauer an.

        private static Dictionary<string, object> GetModul(string pFileName, Type pTypeInterface)
	{
	            //Hier speichern wir unsere Plugins
	            Dictionary<string, object> interfaceinstances = new Dictionary<string, object>();
	 
	            //Loads an assembly given its file name or path.
	            Assembly assembly = Assembly.LoadFrom(pFileName);
	 
	            // http://msdn.microsoft.com/de-de/library/t0cs7xez.aspx
	            // Assembly Eigenschaften checken
	            foreach (Type type in assembly.GetTypes())
	            {
	                if (type.IsPublic) // Ruft einen Wert ab, der angibt, ob der Type als öffentlich deklariert ist. 
	                {
	                    if (!type.IsAbstract)  //nur Assemblys verwenden die nicht Abstrakt sind
	                    {
	                        // Sucht die Schnittstelle mit dem angegebenen Namen. 
	                        Type typeInterface = type.GetInterface(pTypeInterface.ToString(), true);
	 
	                        //Make sure the interface we want to use actually exists
	                        if (typeInterface != null)
	                        {
	                            try
	                            {
	 
	                                object activedInstance = Activator.CreateInstance(type);
	                                if (activedInstance != null)
	                                {
	                                    interfaceinstances.Add(type.Name, activedInstance);
	                                }
	                            }
	                            catch (Exception exception)
	                            {
	                                System.Diagnostics.Debug.WriteLine(exception);
	                            }
	                        }
	 
	                        typeInterface = null;
	                    }
	                }
	            }
	 
	            assembly = null;
	 
	            return interfaceinstances;
	}

Der Funktion wird der Pfad der Assembly übergeben, die nach Plugins durchsucht werden soll. Zusätzlich muss man den Typ des Plugins übergeben.

Wir laden die Assembly mit Assembly.LoadFrom() damit wir Informationen aus ihr lesen können (Stichwort Reflection). Mit der Funktion GetTypes() erhalten wir alle Typen (Klassen), die in der Assembly gespeichert sind. Als nächstes führen wir ein paar Routineprüfungen durch wie z.B. ob der Typ public ist und nicht abstrakt.

Danach „suchen“ oder besser gesagt holen wir mit der Methode GetInterface() das  Plugin-Interface falls der gerade iterierende Typ das Plugin implementiert hat. Falls kein Interface implementiert wurde ist der Rückgabewert NULL. Deshalb wird als nächstes überprüft ob der Rückgabewert ungleich NULL ist. Haben wir einen Rückgabewert erhalten, erstellen wir mit Activator.CreateInstance eine Instanz. Diese speichern wir dann in unser Dictionary.

So werden die Plugins Schritt für Schritt in unsere Anwendung geladen. Wurden alle Dateien auf Plugins überprüft, kann man mit der Funktion public string Calc(string pInput) Mathematikfunktionen aufrufen bzw. ausführen.

Ruft man _MathCore.Calc(“sqrt(3)”) auf, wird in der Funktion Calc der Input String interpretiert damit man weiß, welche Mathematikfunktion aufgerufen wird. In diesem Fall wäre das „sqrt“. Anschließend wird nach dem passenden Plugin mit der Funktion GetPlugin gesucht. Wurde es gefunden, wird der Input String an das Plugin weitergereicht und die Funktion Calc vom Plugin aufgerufen.

Wurde kein Plugin gefunden, wird “Funktion nicht gefunden”  zurückgegeben.

Wie gesagt handelt es sich hierbei um ein sehr einfaches Plugin-System. Das Beispiel verwendet die Plugins zwar nur in einer Konsolen Applikation, trotzdem sollte es kein Problem sein, das Ganze in WPF oder WinForms umzusetzen.

Bei einem ausgereiften Plugin-System gibt es Features wie z.B., dass die Plugins auf bestimmte Teile des Host System zugreifen können (z.b auf ein Control), dass Plugins wiederum Plugins enthalten können oder, dass die Plugins in einer Art Sandbox (Stichwort AppDomain ) gehostet werden damit kein schädlicher Code ausgeführt werden kann usw. usf.

Seit .NET 2< wurden eine Reihe neuer Techniken mitgeliefert die das Erstellen von Plugins vereinfachen soll.

Anbei ein paar Links:

http://www.mycsharp.de/wbb2/thread.php?threadid=34472
http://www.codeproject.com/KB/cs/pluginsincsharp.aspx

Beispiel Projekt herunterladen


Page 2 of 3123

Tray Icon mit WPF

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

Es gibt zwei verbreitete Methoden ein Tray Icon mit WPF zu realisieren. Die erste ist man erstellt im WPF Window das Tray Icon Objekt. Darin wird das Tray Icon Objekt gehostet. Die Nachteile:

  • Code ist nicht gut getrennt Tray Icon / Window
  • Will man nur das Tray Icon zum Programmstart anzeigen muss das Fenster versteckt werden

Die bessere Variante ist, in der App.xaml.cs die Methode OnStartup zu überschreiben und von dort aus das Tray Icon Objekt zu initialisieren. Nun kann man beliebig viele Fenster über das Tray Icon erstellen.

Das ganze realisiert man so:

  1. Projekttyp “Neue WPF Anwendung” erstellen
  2. Das vorhandene Window kann man wie man will löschen oder stehen lassen, wenn man dieses später benötigt
  3. In der App.xaml entfernen wir den Eintrag StartupUri=”Window1.xaml”
  4. Damit verhindern wir, dass beim Programmstart das Fenster Window1 erstellt und angezeigt wird.
  5. Für das TrayIcon verwenden wir das Objekt NotifyIcon welches sich in der Libary System.Windows.Forms befindet.
  6. Wir fügen also zu den Verweisen ->Rechter Mausklick -> Verweis hinzufügen -> System.Windows.Forms und System.Drawing hinzu damit wir auch das Icon setzen können

Ich habe eine kleine Tray Icon Klasse geschrieben, damit man schnell ein paar ContextMenu einträge zum Tray Icon hinzufügen kann. Dazu erstellen wir uns eine neue Klassen-Datei namens Tray.cs und fügen folgenden Code ein.

        using System.Windows.Forms;
	using System;
	 
	public class Tray
	{
	    private NotifyIcon _notico;
	    private bool _Animate = false;
	    private ContextMenu _contextMenu = new ContextMenu();
	 
	    public Tray()
	    {
	        Initialize();
	    }
	    public Tray(System.Drawing.Icon pIcon)
	    {
	        Initialize();
	        _notico.Icon = pIcon;
	    }
	    /// <summary>
	    /// Initialisiert das NotifyIcon
	    /// </summary>
	    private void Initialize()
	    {
	        // NotifyIcon erzeugen
	        _notico = new NotifyIcon();
	        _notico.Visible = true;
	 
	        ContextMenu contextMenu = new ContextMenu();
	 
	        // Kontextmenüeinträge erzeugen
	 
	        _notico.ContextMenu = _contextMenu;
	 
	    }
	    public void CreateMenuItem(String pName)
	    {
	        MenuItem menuItem = new MenuItem();
	        menuItem = new MenuItem();
	        menuItem.Index = 1;
	        menuItem.Name = pName;
	        menuItem.Text = "&" + menuItem.Name;
	 
	        _contextMenu.MenuItems.Add(menuItem);
	    }
	    public void CreateMenuItem(String pName, bool pTrue)
	    {
	        MenuItem menuItem = new MenuItem();
	        menuItem.Index = 2;
	        menuItem.Name = pName;
	        menuItem.Text = "&" + menuItem.Name;
	        menuItem.Click += (sender, e) =>
	        {
	            MenuItem m = (MenuItem)sender;
	            m.Checked = !m.Checked;
	        };
	        menuItem.Checked = pTrue;
	 
	        _contextMenu.MenuItems.Add(menuItem);
	    }
	    public NotifyIcon NotifyIcon
	    {
	        get
	        {
	            return _notico;
	        }
	    }
	}

Jetzt öffnen wir die App.xaml.cs Datei und überschreiben die Methode OnStartUp().

Damit unser Programm nicht beendet wird, wenn man ein erstelltes Fenster schließt, müssen wir die Propertie

ShutdownMode

entsprechend setzen. Wir setzen die Propertie deshalb auf den Wert 

OnExplicitShutdown

.

Damit das Tray Icon im Tray überhaupt sichtbar ist, benötigen wir ein Icon. Man kann ein neues Icon direkt im Vs.net erstellen oder ein vorhandenes hinzufügen. Wie man das Icon ins Programm ladet bleibt jedem selber überlassen. Ich lade das Icon einfachheitshalber aus einer Datei.

Danach erstellen wir das Tray Objekt und übergeben das Icon, welches ich aus der Datei geladen habe.

Nun können wir, auf die Tray Methoden zugreifen und beliebig viele ContextMenüs erstellen.

Über die Propertie NotifyIcon der Klasse Tray können wir auf die MenüItems zugreifen und deren verhalten bestimmen. Z.B, wenn man auf ein Menüitem klickt, dass ein neues Fenster erstellt wird, oder dass die komplette Applikation mit samt Tray Icon geschlossen wird.

Das sieht dann so aus:

        using System;
	using System.Collections.Generic;
	using System.Configuration;
	using System.Data;
	using System.Linq;
	using System.Windows;
	using System.Drawing;
	 
	namespace Tray_Icon_WPF
	{
	    /// <summary>
	    /// Interaction logic for App.xaml
	    /// </summary>
	    public partial class App : Application
	    {
	        protected override void OnStartup(StartupEventArgs e)
	        {
	            this.ShutdownMode = ShutdownMode.OnExplicitShutdown;
	 
	            //Icon laden
	            Icon icon = Icon.ExtractAssociatedIcon(@"C:UsersmartinDocumentsVisual Studio 2008BlogTray Icon WPFTray Icon WPFIcon1.ico"); 
	 
	            //trayicon erstellen und icon laden
	            Tray tray = new Tray(icon);
	 
	            tray.CreateMenuItem("Fenster aufrufen");
	            tray.CreateMenuItem("Beenden");
	 
	            //Menuitem Fenster aufrufen suchen, zugreifen Click Event anmelden und delegate setzen
	            tray.NotifyIcon.ContextMenu.MenuItems.Find("Fenster aufrufen", true).First().Click += (sender, eargs) =>
	            {
	                Window1 window = new Window1();
	                window.Show();
	 
	            };
	 
	            //Menuitem Beenden suchen, zugreifen Click Event anmelden und delegate setzen
	            tray.NotifyIcon.ContextMenu.MenuItems.Find("Beenden", true).First().Click += (sender, eargs) =>
	            {
	                //Icon aus tray löschen
	                tray.NotifyIcon.Dispose();
	                Environment.Exit(0);
	            };

	            base.OnStartup(e);
	        }
	    }
	}

Das Demo Projekt kann man hier herunterladen. Wenn ihr die Demo ausführt, müsst ihr den Pfad zum Icon anpassen. Über Fragen, Anregungen, Kritik freu ich mich.

Links:
Guter Artikel über das Tray Icon, allerdings wird der ShutdownMode nicht erwähnt
http://www.codecomplete.de/blogs/xamlblog/archive/2008/12/15/wpfanwendung-mit-trayicon.aspx
So sollte es meiner Meinung nicht machen (Code im Window)
http://possemeeg.wordpress.com/2007/09/06/minimize-to-tray-icon-in-wpf/


Page 2 of 3123

MarkupExtension – Bilder aus Resource direkt ins XAML laden

By
on Regards: .NET Framework; Archive; 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 zwei Konstruktoren und die Methode ProvideValue implementieren.

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.

Verwendung wie folgt:

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

Syntax: 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.

Hier noch ein Artikel zur Konvertierung von Bildern (jpg, png, …) in eine ImageSource.


Page 2 of 3123

ListBox – SelectedItem Zusatzinformationen anzeigen

By
on Regards: Archive; C#; XAML;

Manchmal ist es praktisch in einer ListBox beim SelectedItem zusätzlich noch mehr Informationen anzuzeigen.

Hier ein Beispiel:

public class Class
{
     public string Artikelname { get; set; }
     public string Foto { get; set; }
}

Es soll nur die Property Artikelname in der ListBox angezeigt werden. Wurde ein Item “Selected” wäre es toll, wenn zusätzlich noch das Foto vom Artikel eingeblendet wird. Im DataTemplate habe ich zwei TextBlöcke die den Inhalt vom Artikelname und vom Foto anzeigen. Der TextBlock der das Foto representiert wird ausgeblendet. Erst wenn das Item “Selected” ist, soll der TextBlock eingeblendet werden. Das Problem im DataTemplate von der Class ist, dass wir im aktuellen DataContext nicht abfragen können, ob das aktuell gewählte Item Selected ist. Der DataContext enthält nämlich die Klasse Class (Zugegebenermaßen der Name ist nicht gut gewählt). Wir benötigen aber den DataContext vom ListBoxItem, da es dort eine Property für IsSelected gibt.

Über die RelativSource kann man auf das ListBoxItem zugreifen, und somit die Property IsSelected abfragen.
Per DataTrigger schalten wir, dann die Visibility.

<DataTemplate DataType="{x:Type local:Class}">
            <StackPanel Orientation="Vertical" >
                <TextBlock Text="{Binding Artikelname}"></TextBlock>
                <TextBlock x:Name="Foto" Text="{Binding Foto}" Visibility="Collapsed"></TextBlock>
            </StackPanel>
            <DataTemplate.Triggers>
                <DataTrigger Binding="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ListBoxItem}}, Path=IsSelected}" Value="True">
                    <Setter TargetName="Foto" Property="Visibility" Value="Visible"></Setter>
                </DataTrigger>
            </DataTemplate.Triggers>
</DataTemplate>

Das ging relativ einfach. Ein anderer Lösungsweg wäre, die Zusatzinformationen über den ItemTemplateSelector an zu zeigen. Wie man die Zusatzinformationen über den ItemTemplateSelector anzeigt wird hier erklärt.

http://www.developingfor.net/net/dynamically-switch-wpf-datatemplate.html


Page 2 of 3123