Festlegen der WPF-Bildquelle im Code

325

Ich versuche, die Quelle eines WPF-Bildes im Code festzulegen. Das Bild wird als Ressource in das Projekt eingebettet. Anhand von Beispielen habe ich den folgenden Code gefunden. Aus irgendeinem Grund funktioniert es nicht - das Bild wird nicht angezeigt.

Durch das Debuggen kann ich sehen, dass der Stream die Bilddaten enthält. Also, was ist falsch?

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;

Das Symbol ist ungefähr so ​​definiert: <Image x:Name="_icon" Width="16" Height="16" />

Torbjørn
quelle
Wenn sich das Image auf einem lokalen Laufwerk befindet, <Image Source="some_fully_qualified_path">schlägt die XAML niemals fehl.
Laurie Stearn
@LaurieStearn der springende Punkt ist, dass wir den Pfad nicht kennen und Code benötigen, um ihn zu bestimmen. Als jemand, der neu in der Windows-GUI-Programmierung ist, muss ich zugeben, dass WinForms ansprechender erscheint als dieser XAML-Mist.
Alex Jansen

Antworten:

415

Nachdem ich das gleiche Problem wie Sie hatte und etwas gelesen hatte, entdeckte ich die Lösung - Pack URIs .

Ich habe im Code Folgendes getan:

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;

Oder kürzer, indem Sie einen anderen BitmapImage-Konstruktor verwenden:

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

Die URI ist in Teile unterteilt:

  • Behörde: application:///
  • Pfad: Der Name einer Ressourcendatei, die zu einer Assembly zusammengestellt wird, auf die verwiesen wird. Der Pfad muss dem folgenden Format entsprechen:AssemblyShortName[;Version][;PublicKey];component/Path

    • AssemblyShortName: Der Kurzname für die Assembly, auf die verwiesen wird.
    • ; Version [optional]: Die Version der Assembly, auf die verwiesen wird und die die Ressourcendatei enthält. Dies wird verwendet, wenn zwei oder mehr referenzierte Assemblys mit demselben Kurznamen geladen werden.
    • ; PublicKey [optional]: Der öffentliche Schlüssel, mit dem die referenzierte Assembly signiert wurde. Dies wird verwendet, wenn zwei oder mehr referenzierte Assemblys mit demselben Kurznamen geladen werden.
    • ; component: Gibt an, dass auf die Assembly, auf die verwiesen wird, von der lokalen Assembly verwiesen wird.
    • / Pfad: Der Name der Ressourcendatei einschließlich ihres Pfads relativ zum Stammverzeichnis des Projektordners der referenzierten Assembly.

Die drei Schrägstriche danach application:müssen durch Kommas ersetzt werden:

Hinweis: Die Berechtigungskomponente eines Paket-URI ist ein eingebetteter URI, der auf ein Paket verweist und RFC 2396 entsprechen muss. Außerdem muss das Zeichen "/" durch das Zeichen "," und reservierte Zeichen wie "%" ersetzt werden. und "?" muss entkommen werden. Einzelheiten finden Sie im OPC.

Und natürlich stellen Sie sicher, dass Sie die Build-Aktion für Ihr Image auf setzen Resource.

Jared Harley
quelle
Ja, dies war die Lösung, die ich nach einigem Ausprobieren gefunden habe. Danke für die gründliche Erklärung. Antwort akzeptiert!
Torbjørn
Ich weiß nicht, warum dies nötig war und warum die anderen Antworten bei mir nicht funktionierten ...
Torbjørn
Die anderen Antworten in dieser und anderen Fragen haben auch bei mir nicht funktioniert. Dieser funktioniert perfekt. Vielen Dank.
Thomas Stock
9
Okay, aber gibt es keine Methode oder Klasse, mit der der XAML-Parser die einfache Pfadzeichenfolge in eine ImageSource konvertiert? Könnten wir das nicht einfach benutzen?
Qwertie
1
Ich sehe dieses Snippet oft in andere Posts kopiert, in denen die Leute normalerweise die Existenz des BitmapImage(Uri)Konstruktors ignorieren , was dazu beiträgt, die Notwendigkeit zu vermeiden, BeginInit und EndInit aufzurufen. Ich habe es hier hinzugefügt.
Clemens
174
var uriSource = new Uri(@"/WpfApplication1;component/Images/Untitled.png", UriKind.Relative);
foo.Source = new BitmapImage(uriSource);

Dadurch wird ein Bild mit dem Namen "Untitled.png" in einen Ordner mit dem Namen "Images" geladen, dessen "Build Action" in einer Assembly mit dem Namen "WpfApplication1" auf "Resource" gesetzt ist.

Simon
quelle
16
Dank dafür. Ein Problem, das mich als Neuling bei wpf auslöste, ist, dass das Bild als Ressource markiert sein muss, damit dies funktioniert.
Si618
4
Diese Lösung gab mir eine Ausnahme, also habe ich Jared Harleys Lösung ausprobiert und es hat funktioniert ...
Sangram Nandkhile
2
Das Wichtigste ist "UriKind.RelativeOrAbsolute" - andere Beispiele warfen immer Ausnahmen
Sven
9
var uriSource = new Uri("Images/Untitled.png", UriKind.Relative);würde ausreichen
Hamzeh Soboh
... und viel einfacher. Danke Hamzeh
pStan
74

Dies ist etwas weniger Code und kann in einer einzigen Zeile ausgeführt werden.

string packUri = "pack://application:,,,/AssemblyName;component/Images/icon.png";
_image.Source = new ImageSourceConverter().ConvertFromString(packUri) as ImageSource;
Alex B.
quelle
8
Dies ist definitiv die beste Antwort unter Verwendung der .NET-eigenen Implementierung
Joshcomley
Was ist, wenn wir die Höhe und Breite des Bildes einstellen müssen, dh icon.png? Denn durch die Verwendung von _image.height und _image.width wird das Bildfenster und nicht das tatsächliche Bild festgelegt, dh icon.png.
secretgenes
41

Sehr leicht:

Führen Sie nur die folgenden Schritte aus, um das Bild eines Menüelements dynamisch festzulegen:

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

... während "icon.ico" überall zu finden ist (derzeit befindet es sich im Verzeichnis "Ressourcen") und als Ressource verknüpft werden muss ...

Ein Bothe
quelle
2
Kürzer = besser. Ich musste es zwar als @ "/ folder / image.png" einstellen, aber dann funktionierte es gut. Vielen Dank!
Gjvdkamp
3
Wenn ich die Bildquelle zuweise, wird sie nur leer
angezeigt
@ HaloMediaz Pfad kann falsch sein .... zB Sie haben Ressourcen, aber Sie können es schreiben Ressource
Rouzbeh Zarandi
Dies funktionierte für mich und war weitaus einfacher als die absoluten URIs in den anderen Antworten.
Psiloc
16

Sie können dies auch auf eine Zeile reduzieren. Dies ist der Code, mit dem ich das Symbol für mein Hauptfenster festgelegt habe. Es wird davon ausgegangen, dass die .ico-Datei als Inhalt markiert ist und in das Ausgabeverzeichnis kopiert wird.

 this.Icon = new BitmapImage(new Uri("Icon.ico", UriKind.Relative));
Payson Welch
quelle
16

Einfachster Weg:

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

Hast du es versucht:

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

Das ist mein Weg:

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);
    }
}

Verwendungszweck:

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

Hier ist ein Beispiel, das den Bildpfad dynamisch festlegt (Bild befindet sich irgendwo auf der Disc, anstatt als Ressource zu erstellen):

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;
}

Reflexionen über Symbole ...

Zuerst würden Sie denken, dass die Icon-Eigenschaft nur ein Bild enthalten kann. Aber es kann tatsächlich alles enthalten! Ich habe dies zufällig entdeckt, als ich programmgesteuert versuchte, die ImageEigenschaft direkt auf eine Zeichenfolge mit dem Pfad zu einem Bild festzulegen. Das Ergebnis war, dass nicht das Bild, sondern der eigentliche Text des Pfades angezeigt wurde!

Dies führt zu einer Alternative, bei der kein Bild für das Symbol erstellt werden muss, sondern stattdessen Text mit einer Symbolschrift verwendet wird, um ein einfaches "Symbol" anzuzeigen. Im folgenden Beispiel wird die Wingdings-Schriftart verwendet, die ein "Disketten" -Symbol enthält. Dieses Symbol ist wirklich das Zeichen <, das in XAML eine besondere Bedeutung hat, daher müssen wir &lt;stattdessen die codierte Version verwenden. Das funktioniert wie ein Traum! Das Folgende zeigt ein Diskettensymbol als Symbol im Menüpunkt:

<MenuItem Name="mnuFileSave" Header="Save" Command="ApplicationCommands.Save">
  <MenuItem.Icon>
    <Label VerticalAlignment="Center" HorizontalAlignment="Center" FontFamily="Wingdings">&lt;</Label>
  </MenuItem.Icon>
</MenuItem>
Ehrfurcht
quelle
Frage: " Das Image ist als Ressource in das Projekt eingebettet ", Antwort: " Hier ist ein Beispiel [für ein Image], das sich irgendwo auf der Disc befindet und nicht als Ressource erstellt wird. "
Minuten
7

Wenn Ihr Bild in einem ResourceDictionary gespeichert ist, können Sie dies mit nur einer Codezeile tun:

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

Es gibt auch einen einfacheren Weg. Wenn das Bild als Ressource in die XAML geladen wird und der betreffende Code der Code-Behind für diesen XAML-Inhalt ist:

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

Legen Sie den Rahmen in einen VisualBrush:

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

brush.Visual = frame;

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

Fügen Sie den VisualBrush in GeometryDrawing ein

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;

Fügen Sie nun das GeometryDrawing in ein DrawingImage ein:

new DrawingImage(drawing);

Platzieren Sie dies auf Ihrer Bildquelle und voilà!

Sie könnten es aber viel einfacher machen:

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

Und im Code:

BitmapImage image = new BitmapImage { UriSource="/yourassembly;component/YourImage.PNG" };
Arcturus
quelle
LOL! Warum es einfach machen, wenn Sie es zuerst schwierig machen können :) Ich werde Ihre einfache Lösung ausprobieren, bevor ich sie akzeptiere ...
Torbjørn
Eigentlich hat mir das überhaupt nicht geholfen. Vielleicht bin ich dumm :( Ich habe momentan keine Zeit, genauer hinzuschauen (Haustierprojekt). Ich hätte gerne mehr Antworten, wenn ich darauf zurückkomme :)
Torbjørn
3

Es gibt auch einen einfacheren Weg. Wenn das Bild als Ressource in die XAML geladen wird und der betreffende Code der Code für diese XAML ist:

Hier ist das Ressourcenwörterbuch für eine XAML-Datei - die einzige Zeile, die Sie interessiert, ist der ImageBrush mit dem Schlüssel "PosterBrush" - der Rest des Codes dient nur zur Anzeige des Kontexts

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

        </ResourceDictionary>
    </UserControl.Resources>

Im Code dahinter können Sie dies jetzt einfach tun

ImageBrush posterBrush = (ImageBrush)Resources["PosterBrush"];
Mark Mullin
quelle
2

So laden Sie ein Bild aus eingebetteten Ressourcensymbolen und -bildern (korrigierte Version von Arcturus):

Angenommen, Sie möchten eine Schaltfläche mit einem Bild hinzufügen. Was sollte man tun?

  1. Fügen Sie dem Projektordner Symbole hinzu und fügen Sie hier das Bild ClickMe.png ein
  2. Setzen Sie in den Eigenschaften von 'ClickMe.png' 'BuildAction' auf 'Ressource'.
  3. Angenommen, Ihr Name für die kompilierte Assembly lautet "Company.ProductAssembly.dll".
  4. Jetzt ist es Zeit, unser Bild in XAML zu laden

    <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>

Erledigt.

Siarhei Kuchuk
quelle
2

Ich bin neu in WPF, aber nicht in .NET.

Ich habe fünf Stunden lang versucht, eine PNG-Datei zu einem "WPF Custom Control Library-Projekt" in .NET 3.5 (Visual Studio 2010) hinzuzufügen und als Hintergrund für ein von Bildern vererbtes Steuerelement festzulegen.

Es hat nichts mit URIs zu tun. Ich kann mir nicht vorstellen, warum es keine Methode gibt, um einen URI aus einer Ressourcendatei über IntelliSense abzurufen, vielleicht als:

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

Ich habe viele URIs ausprobiert und mit ResourceManager und GetManifest-Methoden von Assembly gespielt, aber es gab nur Ausnahmen oder NULL-Werte.

Hier schreibe ich den Code, der für mich funktioniert hat:

// 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;
JoanComasFdz
quelle
3
Ich denke, das Problem ist, dass WPF den traditionellen Ansatz, Ressourcen in Resx-Dateien zu speichern, nicht "mag". Stattdessen sollten Sie das Bild direkt zu Ihrem Projekt hinzufügen und seine Build Action-Eigenschaft auf Resource setzen. Ich weiß nicht, was der Unterschied (im physischen Dateiformat) zwischen den beiden Ansätzen ist.
Qwertie
1
Stellen Sie sicher, dass die Build-Aktion zufrieden ist
Jerry Nixon
1

Wenn Sie bereits einen Stream haben und das Format kennen, können Sie Folgendes verwenden:

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

quelle
1

Du hast nur ein bisschen verpasst.

Um eine eingebettete Ressource von einer Assembly zu erhalten, müssen Sie den Assemblynamen mit Ihrem Dateinamen angeben, wie ich hier erwähnt habe:

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;
Maulik Kansara
quelle
BeginInit () scheint in WPF nicht zu existieren.
Myosotis
Es ist verfügbar unter folgendem Link msdn.microsoft.com/en-us/library/…
maulik kansara