364

I'm trying to set a WPF image's source in code. The image is embedded as a resource in the project. By looking at examples I've come up with the below code. For some reason it doesn't work - the image does not show up.

By debugging I can see that the stream contains the image data. So what's wrong?

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;

The icon is defined something like this: <Image x:Name="_icon" Width="16" Height="16" />

Arcturus
  • 26,677
  • 10
  • 92
  • 107
Torbjørn
  • 6,423
  • 5
  • 29
  • 42
  • If the image is on a local drive, `` in the XAML never fails. – Laurie Stearn Dec 31 '17 at 05:10
  • 1
    @LaurieStearn the whole point is that we don't know the path and need code in order to determine it. As somebody new to Windows GUI programming, I have to admit that WinForms seems more appealing than this XAML crap. – Alex Jansen Mar 30 '19 at 08:05

19 Answers19

463

After having the same problem as you and doing some reading, I discovered the solution - Pack URIs.

I did the following in code:

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;

Or shorter, by using another BitmapImage constructor:

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

The URI is broken out into parts:

  • Authority: application:///
  • Path: The name of a resource file that is compiled into a referenced assembly. The path must conform to the following format: AssemblyShortName[;Version][;PublicKey];component/Path

    • AssemblyShortName: the short name for the referenced assembly.
    • ;Version [optional]: the version of the referenced assembly that contains the resource file. This is used when two or more referenced assemblies with the same short name are loaded.
    • ;PublicKey [optional]: the public key that was used to sign the referenced assembly. This is used when two or more referenced assemblies with the same short name are loaded.
    • ;component: specifies that the assembly being referred to is referenced from the local assembly.
    • /Path: the name of the resource file, including its path, relative to the root of the referenced assembly's project folder.

The three slashes after application: have to be replaced with commas:

Note: The authority component of a pack URI is an embedded URI that points to a package and must conform to RFC 2396. Additionally, the "/" character must be replaced with the "," character, and reserved characters such as "%" and "?" must be escaped. See the OPC for details.

And of course, make sure you set the build action on your image to Resource.

Clemens
  • 123,504
  • 12
  • 155
  • 268
Jared Harley
  • 8,219
  • 4
  • 39
  • 48
  • Yes, this was the solution I found myself after some trial and error. Thanks for the thorough explanation. Answer accepted! – Torbjørn Nov 04 '09 at 19:35
  • I don't know why this was needed though, and why the other answers didn't work for me... – Torbjørn Nov 04 '09 at 19:37
  • the other answers in this and other questions didn't work for me either. This one works perfectly. Thanks. – Thomas Stock Mar 27 '10 at 13:44
  • 11
    Okay, but isn't there some method or class that the XAML parser uses to convert the simple path string to an ImageSource? Couldn't we just use that? – Qwertie Jun 02 '11 at 16:48
  • This solution doesn't work for me. I get a compile error: `Cannot implicitly convert type System.Windows.Media.Imaging.BitmapImage to System.Windows.Controls.Image`... – Scott Solmer Jul 24 '14 at 16:43
  • Add the image to the grid from toolbox, add a name to it (e.g. TestImage) and set the image path from properties menu. Then write var **testImage = TestImage** to startup code, add a breakpoint, debug and copy the source value. – hkarask Nov 06 '14 at 07:48
  • 1
    I often see this snippet copied into other posts, where people usually ignore the existence of the `BitmapImage(Uri)` constructor, which helps to avoid the need for calling BeginInit and EndInit. I've added it here. – Clemens Nov 07 '17 at 06:51
  • Where do i get the assembly name from? – Yessir Feb 17 '21 at 11:36
192
var uriSource = new Uri(@"/WpfApplication1;component/Images/Untitled.png", UriKind.Relative);
foo.Source = new BitmapImage(uriSource);

This will load a image called "Untitled.png" in a folder called "Images" with its "Build Action" set to "Resource" in an assembly called "WpfApplication1".

Simon
  • 33,714
  • 21
  • 133
  • 202
76

This is a bit less code and can be done in a single line.

string packUri = "pack://application:,,,/AssemblyName;component/Images/icon.png";
_image.Source = new ImageSourceConverter().ConvertFromString(packUri) as ImageSource;
Alex B
  • 2,766
  • 1
  • 17
  • 8
  • 9
    This is definitely the best answer, utilising .NETs own implementation – joshcomley Apr 27 '14 at 17:30
  • what if we need to set the height and width of image i.e icon.png? Because by using _image.height and _image.width it sets the image window not the actual image i.e icon.png. – secretgenes Feb 18 '16 at 05:12
50

Very easy:

To set a menu item's image dynamically, only do the following:

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

...whereas "icon.ico" can be located everywhere (currently it's located in the 'Resources' directory) and must be linked as Resource...

Alexander Farber
  • 21,519
  • 75
  • 241
  • 416
A Bothe
  • 501
  • 4
  • 2
21

Simplest way:

var uriSource = new Uri("image path here");
image1.Source = new BitmapImage(uriSource);
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Hasan
  • 335
  • 2
  • 10
19

This is my way:

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

Usage:

new BitmapImage(ResourceAccessor.Get("Images/1.png"))
Manfred
  • 5,320
  • 3
  • 35
  • 29
IlPADlI
  • 1,943
  • 18
  • 22
17

You can also reduce this to one line. This is the code I used to set the Icon for my main window. It assumes the .ico file is marked as Content and is being copied to the output directory.

 this.Icon = new BitmapImage(new Uri("Icon.ico", UriKind.Relative));
Payson Welch
  • 1,388
  • 2
  • 17
  • 29
15

Have you tried:

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
  • 1,444
  • 3
  • 18
  • 27
9

If your image is stored in a ResourceDictionary, you can do it with only one line of code:

MyImage.Source = MyImage.FindResource("MyImageKeyDictionary") as ImageSource;
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Hollyroody
  • 91
  • 1
  • 3
8

Here is an example that sets the image path dynamically (image located somewhere on disc rather than build as resource):

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

Reflections on Icons...

First thought, you would think that the Icon property can only contain an image. But it can actually contain anything! I discovered this by accident when I programmatically tried to set the Image property directly to a string with the path to an image. The result was that it did not show the image, but the actual text of the path!

This leads to an alternative to not have to make an image for the icon, but use text with a symbol font instead to display a simple "icon". The following example uses the Wingdings font which contains a "floppydisk" symbol. This symbol is really the character <, which has special meaning in XAML, so we have to use the encoded version &lt; instead. This works like a dream! The following shows a floppydisk symbol as an icon on the menu item:

<MenuItem Name="mnuFileSave" Header="Save" Command="ApplicationCommands.Save">
  <MenuItem.Icon>
    <Label VerticalAlignment="Center" HorizontalAlignment="Center" FontFamily="Wingdings">&lt;</Label>
  </MenuItem.Icon>
</MenuItem>
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
awe
  • 21,938
  • 6
  • 78
  • 91
  • Question: "*The image is embedded as a resource in the project*", answer: "*Here is an example [for an image] located somewhere on disc rather than build as resource*". – mins Sep 29 '19 at 11:30
7

There's also a simpler way. If the image is loaded as a resource in the XAML, and the code in question is the code-behind for that XAML content:

Uri iconUri = new Uri("pack://application:,,,/ImageNAme.ico", UriKind.RelativeOrAbsolute);
NotifyIcon.Icon = BitmapFrame.Create(iconUri);
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
6

Put the frame in a VisualBrush:

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

brush.Visual = frame;

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

Put the VisualBrush in GeometryDrawing

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;

Now put the GeometryDrawing in a DrawingImage:

new DrawingImage(drawing);

Place this on your source of the image, and voilà!

You could do it a lot easier though:

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

And in code:

BitmapImage image = new BitmapImage { UriSource="/yourassembly;component/YourImage.PNG" };
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Arcturus
  • 26,677
  • 10
  • 92
  • 107
  • LOL! Why make it easy when you first can make it difficult :) I'll try your simple solution before I accept though... – Torbjørn Dec 09 '08 at 07:13
  • Actually this didn't help me at all. Maybe I'm stupid :( Don't have time to look more closely right now (pet project). Would like more answers for when I get back to it :) – Torbjørn Dec 09 '08 at 13:51
4

There's also a simpler way. If the image is loaded as a resource in the XAML, and the code in question is the codebehind for that XAML:

Here's the resource dictionary for a XAML file - the only line you care about is the ImageBrush with the key "PosterBrush" - the rest of the code is just to show context

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

        </ResourceDictionary>
    </UserControl.Resources>

Now, in the code behind, you can just do this

ImageBrush posterBrush = (ImageBrush)Resources["PosterBrush"];
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Mark Mullin
  • 1,340
  • 1
  • 9
  • 21
2

How to load an image from embedded in resource icons and images (corrected version of Arcturus):

Suppose you want to add a button with an image. What should you do?

  1. Add to project folder icons and put image ClickMe.png here
  2. In properties of 'ClickMe.png', set 'BuildAction' to 'Resource'
  3. Suppose your compiled assembly name is 'Company.ProductAssembly.dll'.
  4. Now it's time to load our image in XAML

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

Done.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Siarhei Kuchuk
  • 5,296
  • 1
  • 28
  • 31
2

You just missed a little bit.

To get an embedded resource from any assembly, you have to mention the assembly name with your file name as I have mentioned here:

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;
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
maulik kansara
  • 1,087
  • 6
  • 21
2

I am a new to WPF, but not in .NET.

I have spent five hours trying to add a PNG file to a "WPF Custom Control Library Project" in .NET 3.5 (Visual Studio 2010) and setting it as a background of an image-inherited control.

Nothing relative with URIs worked. I can not imagine why there is no method to get a URI from a resource file, through IntelliSense, maybe as:

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

I've tried a lot of URIs and played with ResourceManager, and Assembly's GetManifest methods, but all there were exceptions or NULL values.

Here I pot the code that worked for me:

// 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;
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
JoanComasFdz
  • 2,911
  • 5
  • 34
  • 50
  • 3
    I think the problem is that WPF doesn't "like" the traditional approach of putting resources in resx files. Instead you are supposed to add the image directly to your project and set its Build Action property to Resource. I'm don't know what the difference is (in term physical file format) between the two approaches. – Qwertie Jun 03 '11 at 17:08
  • 1
    Ensure the Build Action is Content – Jerry Nixon Nov 18 '11 at 17:21
1

If you already have a stream and know the format, you can use something like this:

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

Here is if you want to locate it next to your executable (relative from the executable)

img.Source = new BitmapImage(new Uri(AppDomain.CurrentDomain.BaseDirectory + @"\Images\image.jpg", UriKind.Absolute));
Donovan Phoenix
  • 1,351
  • 1
  • 10
  • 8
-1

Force chose UriKind will be correct:

Image.Source = new BitmapImage(new Uri("Resources/processed.png", UriKind.Relative));

UriKind options:

UriKind.Relative // relative path
UriKind.Absolute // exactly path