none
Bild, wie "verstecken" RRS feed

  • Frage

  • Hallo,

    Meine Frage handelt dieses mal wie kann ich Bilder verstauen, dass sie beim kompilieren keinen Ordner anlegen aber jederzeit benutzt werden können?

    Als Beispiel: Ich habe 2 Bilder. Wenn ich auf Button1 klicke soll Bild1 in einem Grid angezeigt werden. Wenn ich Button2 klicke soll Bild2 im Grid angezeigt werden. Ich möchte das ganze aber nicht mit der GetFullPath Methode, da ich den Usern keinen Zugriff auf die direkten Bilder geben möchte, lediglich nur eine "Skinfunktion" um dadurch das Aussehen des Programms zu ändern.

    Der Gedanke hinter dieser Idee ist folgender: Ich mache einen Layout Ordner und lese immer wenn benötigt ein Design mit der GetFullPath Methode aus. Wenn nun ein User jedoch den Namen ändert, wäre keine Imagesource mehr vorhanden und das Bild kann nicht angezeigt werden. Wenn ich nun irgendwie die Bilder aus Ressourcen anzeigen lassen kann, wenn sie benötigt werden, kann dies nicht passieren.

    Hier ein Beispiel noch wie ich es geschrieben habe, jedoch NICHT möchte:

    ImageBrush myBrush = new ImageBrush();
                    myBrush.ImageSource = new BitmapImage(new Uri(list1.Find(f => f.id == 1).source.ToString()));
                    grid1.Background = myBrush;

    Zur Erklärung dieses Codes:  list1 greift indem Fall auf eine Klasse zu und sucht aus der Itemliste von id=1 den Imagesource raus.

    Was es mir eben etwas verschwert ist, dass ich den Imagesource in der Klasse abspeichern muss, da ich dadurch weiß, welches Item welche ID hat, wenn das ganze aber nun irgendwie in einer Ressourcedatei ist habe ich relativ wenig Ahnung, wie ich dies in meine Klasse schreiben muss um dies auch zum laufen zu bringen.

    Dazu noch meine Klasse:

    public List<Items> Weapons()
            {
                 List<Items> weapons = new List<Items>();
                 weapons.Add(new Items() { id = 1, name = "Katana", attack = 20, speed = 10, dodge = 15, tip = "With this Katana do you slay everything!", source = path + "//Sword1.png" });
                return weapons;
            }
    
    
           public string name { get; set; }
    
            public int id { get; set; }
    
            public int attack { get; set; }
    
            public int hitpoints { get; set; }
    
            public int defense { get; set; }
    
            public int speed { get; set; }
    
            public string tip { get; set; }
    
            public string source { get; set; }
    EDIT: Mir ist klar, das in der Klasse Weapons steht obwohl nur eine Waffe hinzu gefügt wird. Weitere Waffen füge ich ein sobald mein Code passt. Ebenfalls ist mein Code nur zum testen. Den Code schreibe ich natürlich dann passend um.

    Mit freundlichen Grüßen

    Markus Ö.



    Samstag, 14. Juni 2014 21:57

Antworten

  • Hallo zusammen,
    warum sich das laden eines Bildes in WPF so schwer machen?

    image.Source = new BitmapImage(new Uri(@"/myimg.png", UriKind.Relative));

    Das erzeugte BitmapImage kannst du überall in WPF einbringen, wo ein Bild benötigt wird. Ein Problem mit Transparenz gibt es da eigentlich nicht. Wenn doch, poste bitte einen Screenshot davon.

    Auch auf eine RESX-Datei würde ich versuchen unter WPF zu verzichten, die bringt dir eigentlich keine Vorteile.

    Wie viele Bilder hat denn jede Waffe? Wenn jede Waffe nur ein Bild hat, kannst du einfach eine ImageSource-Eigenschaft in der Weapons-Klasse erstellen und diese beim initialisieren zuweisen. Wenn es ein 3D-Modell o.ä. gibt, kannst du auch eine eigene Klasse anlegen, die sämtliche Bilder enthält und eine Instanz dieser Klasse in Weapons einfügen.

    An deiner Stelle würde ich eine XML-Datei o.ä. verwenden um alle Waffen mit ihren Initialisierungswerten abzuspeichern. Darin kannst du auch den Namen des Bildes abspeichern. Beim starten des Spiels kannst du nun die Datei auslesen und dynamisch jede Waffe erstellen und zu deiner Liste hinzufügen.

    <?xml version="1.0" encoding="utf-8" ?>
    <Weapons>
      <Weapon Id="0" Name="Waffe 1" Attack="1"  Speed="10" Image="/Images/Image1.png"></Weapon>
      <Weapon Id="1" Name="Waffe 2" Attack="2"  Speed="15" Image="/Images/Image2.png"></Weapon>
      <Weapon Id="2" Name="Waffe 3" Attack="5"  Speed="20" Image="/Images/Image3.png"></Weapon>
    </Weapons>
    Diese Datei kannst du auch als eingebettete Resource zum Projekt hinzufügen, da die Initiierungswerte nicht geändert werden sollen.
    So kannst du die Liste aus der Datei erstellen:
    List<Weapon> myList = null;
    using (var s = new StreamReader(Assembly.GetExecutingAssembly().GetManifestResourceStream("WpfApplication161.XMLFile1.xml")))//Namespace + Dateiname
    {
        myList = XDocument.Parse(s.ReadToEnd())//XML aus Resourcen laden
            .Root//Weapons-Knoten
            .Elements("Weapon")//Jeder Weapon-Knoten
            .Select(x =>
            new Weapon()//Für jeden Knoten eine neue Waffe erzeugen
            {
                name = x.Attribute("Name").Value,//Attribute auslesen
                attack = int.Parse(x.Attribute("Attack").Value),
                speed = int.Parse(x.Attribute("Speed").Value),
                image = new BitmapImage(new Uri(x.Attribute("Image").Value, UriKind.RelativeOrAbsolute)),
            }).ToList();//Eine List<T> aus der Auflistung machen
    }
    Ich habe das komplett über LINQ gelöst, weil es am einfachsten ist, wenn man weis wie es geht.
    PS: Ich habe in meinem Beispiel die Bilder Image1.png bis Images3.png in dem Ordner Images im Projekt abgelegt. Bei allen Bildern war der Buildvorgang auf "Eingebettete Resource" gestellt, sodass alles direkt in die Anwendung einkompiliert wird. 


    Tom Lambert - C# MVP
    Bitte bewertet- und markiert Beiträge als Antwort. Danke.
    Nützliche Links: .NET Quellcode | C# ↔ VB.NET Konverter
    Ich: Webseite | Code Beispiele | Facebook | Twitter | Snippets


    Sonntag, 15. Juni 2014 13:07
    Moderator

Alle Antworten

  • Hi Markus,

    du könntest die Skins in deiner Anwendung in eine Resource Datei packen und über die
    Eigenschaft "Buildvorgang" als Eingebettete Resource kennzeichnen.

    Resourceneinstellungen:

    1. Dem Projekt eine neue Resourcendatei hinzufügen "Images.resx"
    2. Doppelklick auf die Resourcedatei und bei Resource hinzufügen "Bilder" auswählen
    3. Deine Bilder einfügen z.B. Skin1.png & Skin2.png
    4. Im neuen Projektordner Resourcen die beiden Bilder durch rechte Maustaste "Eigenschaften"
    bei Buildvorgang auf Eingebettete Resource setzen.

    Jetzt kannst du in deinem Code auf die Bilder innerhalb der Resourcendatei verweißen
    ohne dass diese durch die User bearbeitete werden können, da Sie jetzt Bestandteil
    deiner Assembly sind.

    Code:


    Xaml:

    <Window x:Class="ImageAsEmbeddedResource.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            Title="MainWindow" Height="350" Width="525">
        <Grid>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="*" />
                <ColumnDefinition Width="Auto" />
            </Grid.ColumnDefinitions>
            
            <Image x:Name="imgSkin" Grid.Column="0" Margin="5" />
            <StackPanel Orientation="Vertical" Margin="5" Grid.Column="1">
                <Button x:Name="btnSkin1" Click="btnSkin_Click" Margin="5" Content="Skin 1" />
                <Button x:Name="btnSkin2" Click="btnSkin_Click" Margin="5" Content="Skin 2" />
            </StackPanel>
            
        </Grid>
    </Window>

    C#

        public partial class MainWindow : Window
        {
            public MainWindow()
            {
                InitializeComponent();
            }
    
            private void btnSkin_Click(object sender, RoutedEventArgs e)
            {
                Button button = e.OriginalSource as Button;
    
                if (button == null) return;
    
                switch (button.Name)
                {
                    case "btnSkin1":
                        imgSkin.Source = ConvertBitmapToImageSource(Images.Skin1);
                        break;
    
                    case "btnSkin2":
                        imgSkin.Source = ConvertBitmapToImageSource(Images.Skin2);
                        break;
    
                    default:
                        throw new InvalidOperationException("Für diesen Button gibt es noch keine Aktion ...");
                }
            }
    
            private ImageSource ConvertBitmapToImageSource(System.Drawing.Bitmap bitmap)
            {
                if (bitmap == null)
                    throw new ArgumentNullException("bitmap");
    
                // Quelle: http://stackoverflow.com/questions/94456/load-a-wpf-bitmapimage-from-a-system-drawing-bitmap
                using (MemoryStream ms = new MemoryStream())
                {
                    bitmap.Save(ms, ImageFormat.Bmp);
                    ms.Position = 0;
                    BitmapImage bitmapImage = new BitmapImage();
                    bitmapImage.BeginInit();
                    bitmapImage.StreamSource = ms;
                    bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
                    bitmapImage.EndInit();
    
                    return bitmapImage;
                }
            }
        }

    Screenshots:



    Gruß
    JohSu



    • Bearbeitet JohSu Samstag, 14. Juni 2014 23:26
    Samstag, 14. Juni 2014 23:24
  • Hallo,

    erstmal danke für die hilfreiche Antwort. 

    Besteht hierbei auch die Möglichkeit, das ich in meiner Klasse bei 

    source = path + "//Sword1.png"

    dem Pfad in der Ressource eingebe?

    also zB

    source = myproject.Properties.Resources.Sword1

    Und mit dem Pfad eben direkt per zB Buttondruck das Bild ändere?

    Mit freundlichen Grüßen

    Markus Ö.

    Samstag, 14. Juni 2014 23:32
  • Hallo,

    Ich habe mir einige Thema dazu durchgelesen. Ich wüsste den Ansatz jedoch brauche ich hierzu noch kurz Hilfe.

    Mit folgenden Code kann ich ein Bild aus einer Ressource laden:

    Bitmap bmp = new Bitmap(System.Reflection.Assembly.GetEntryAssembly().GetManifestResourceStream("ImageRessource..Resources.Image1.png"));
                

    Wenn ich nun Grid1 dieses Bild zuweißen möchte wie müssten dies aussehen? Da grid1.Background = bmp nicht funktioniert. (Kann nicht von Drawing.Bitmap in MediaBrush konvertiert werden)

    Mit freundlichen Grüßen

    Sonntag, 15. Juni 2014 00:34
  • Hi Bashesz,

    du kannst die Background Eigenschaft des Grids, unter zuhilfenahme der Helpermethode ConvertBitmapToImageSource aus meinem ersten Beispiel wie folgt setzen:

    grid.Background = new ImageBrush(ConvertBitmapToImageSource(Images.Skin1));

    Allerdings musst du die Resourcen nicht extra aus der Assembly laden (es sei denn du willst das bewusst so). Es reicht, wenn du wie oben beschrieben eine neue Resourcen Datei anlegest und die Bilder dann dort einfügst.

    Wenn deine Resource Datei z.B. Images.resx heißt und dein Bild den Name Skin1.png hat, kannst du in deinem Code direct durch den Aufruf von Images.Skin1 auf das Bild zugreifen.

    Gruß

    JohSu

    Sonntag, 15. Juni 2014 01:31
  • Hallo,

    Danke, Das ganze funktioniert so weit so gut. Jedoch ist der Background des Images irgendwie. Das Bild ist natürlich in .png und transparenter Background, jedoch entsteht ein schwarzer Background im grauen streifen darin obwohl es eigentlich transparent sein sollte.

    Mit freundlichen Grüßen

    Markus Ö.


    • Bearbeitet Bashesz Sonntag, 15. Juni 2014 02:10
    Sonntag, 15. Juni 2014 01:59
  • Hallo zusammen,
    warum sich das laden eines Bildes in WPF so schwer machen?

    image.Source = new BitmapImage(new Uri(@"/myimg.png", UriKind.Relative));

    Das erzeugte BitmapImage kannst du überall in WPF einbringen, wo ein Bild benötigt wird. Ein Problem mit Transparenz gibt es da eigentlich nicht. Wenn doch, poste bitte einen Screenshot davon.

    Auch auf eine RESX-Datei würde ich versuchen unter WPF zu verzichten, die bringt dir eigentlich keine Vorteile.

    Wie viele Bilder hat denn jede Waffe? Wenn jede Waffe nur ein Bild hat, kannst du einfach eine ImageSource-Eigenschaft in der Weapons-Klasse erstellen und diese beim initialisieren zuweisen. Wenn es ein 3D-Modell o.ä. gibt, kannst du auch eine eigene Klasse anlegen, die sämtliche Bilder enthält und eine Instanz dieser Klasse in Weapons einfügen.

    An deiner Stelle würde ich eine XML-Datei o.ä. verwenden um alle Waffen mit ihren Initialisierungswerten abzuspeichern. Darin kannst du auch den Namen des Bildes abspeichern. Beim starten des Spiels kannst du nun die Datei auslesen und dynamisch jede Waffe erstellen und zu deiner Liste hinzufügen.

    <?xml version="1.0" encoding="utf-8" ?>
    <Weapons>
      <Weapon Id="0" Name="Waffe 1" Attack="1"  Speed="10" Image="/Images/Image1.png"></Weapon>
      <Weapon Id="1" Name="Waffe 2" Attack="2"  Speed="15" Image="/Images/Image2.png"></Weapon>
      <Weapon Id="2" Name="Waffe 3" Attack="5"  Speed="20" Image="/Images/Image3.png"></Weapon>
    </Weapons>
    Diese Datei kannst du auch als eingebettete Resource zum Projekt hinzufügen, da die Initiierungswerte nicht geändert werden sollen.
    So kannst du die Liste aus der Datei erstellen:
    List<Weapon> myList = null;
    using (var s = new StreamReader(Assembly.GetExecutingAssembly().GetManifestResourceStream("WpfApplication161.XMLFile1.xml")))//Namespace + Dateiname
    {
        myList = XDocument.Parse(s.ReadToEnd())//XML aus Resourcen laden
            .Root//Weapons-Knoten
            .Elements("Weapon")//Jeder Weapon-Knoten
            .Select(x =>
            new Weapon()//Für jeden Knoten eine neue Waffe erzeugen
            {
                name = x.Attribute("Name").Value,//Attribute auslesen
                attack = int.Parse(x.Attribute("Attack").Value),
                speed = int.Parse(x.Attribute("Speed").Value),
                image = new BitmapImage(new Uri(x.Attribute("Image").Value, UriKind.RelativeOrAbsolute)),
            }).ToList();//Eine List<T> aus der Auflistung machen
    }
    Ich habe das komplett über LINQ gelöst, weil es am einfachsten ist, wenn man weis wie es geht.
    PS: Ich habe in meinem Beispiel die Bilder Image1.png bis Images3.png in dem Ordner Images im Projekt abgelegt. Bei allen Bildern war der Buildvorgang auf "Eingebettete Resource" gestellt, sodass alles direkt in die Anwendung einkompiliert wird. 


    Tom Lambert - C# MVP
    Bitte bewertet- und markiert Beiträge als Antwort. Danke.
    Nützliche Links: .NET Quellcode | C# ↔ VB.NET Konverter
    Ich: Webseite | Code Beispiele | Facebook | Twitter | Snippets


    Sonntag, 15. Juni 2014 13:07
    Moderator
  • Hallo,

    Danke, werde ich mir gleich ansehen. Wie das ganze geschrieben wird ist mir eigentlich egal, hauptsache die User haben keinen Zugriff auf die Bilder, obwohl sie vorhanden sind. Da ich aus Dummheit des Menschen keine Fehler haben möchte.

    Mich würde aber interessieren warum eine XML Datei besser wäre als die Klasse? In der Klasse habe ich ebenfalls den Source abgespeichert und lese die Werte beim start aus.

    Bei weiteren Problemen werde ich mich hier wenden.

    EDIT: Und ja jedes Item hat nur 1 Bild.

    Mit freundlichen Grüßen

    Markus Ö.




    • Bearbeitet Bashesz Sonntag, 15. Juni 2014 17:28
    Sonntag, 15. Juni 2014 16:50
  • Mich würde aber interessieren warum eine XML Datei besser wäre als die Klasse? In der Klasse habe ich ebenfalls den Source abgespeichert und lese die Werte beim start aus.
    Hallo,
    das ist Geschmackssache. Die meisten lagern gerne ähnliche Datei aus. Auf diese Weise hast du eine Datei, in der alle Daten enthalten sind. Wenn du nun etwas korrigieren willst, musst du nur die XML-Datei anpassen und nicht erst lange im Code suchen. Auch lassen sich solche Dateien leichter übersetzen.
    Der benötigte Code ist nun auch eher kurz, sodass es IMHO nur Vorteile bringt.

    Tom Lambert - C# MVP
    Bitte bewertet- und markiert Beiträge als Antwort. Danke.
    Nützliche Links: .NET Quellcode | C# ↔ VB.NET Konverter
    Ich: Webseite | Code Beispiele | Facebook | Twitter | Snippets

    Dienstag, 17. Juni 2014 13:33
    Moderator