Storing WPF Image Resources
Asked Answered
M

11

456

For a WPF application which will need 10 - 20 small icons and images for illustrative purposes, is storing these in the assembly as embedded resources the right way to go?

If so, how do I specify in XAML that an Image control should load the image from an embedded resource?

Mulct answered 7/12, 2008 at 14:14 Comment(0)
P
519

If you will use the image in multiple places, then it's worth loading the image data only once into memory and then sharing it between all Image elements.

To do this, create a BitmapSource as a resource somewhere:

<BitmapImage x:Key="MyImageSource" UriSource="../Media/Image.png" />

Then, in your code, use something like:

<Image Source="{StaticResource MyImageSource}" />

In my case, I found that I had to set the Image.png file to have a build action of Resource rather than just Content. This causes the image to be carried within your compiled assembly.

Prase answered 3/3, 2009 at 16:2 Comment(7)
Would it be possible to do this dynamically? If I have a differing number of images that I would like to load on start-up, could I create a BitmapSource per image and refer to them the same way as above?Presentable
@Becky - Yes you could, though if you wanted to refer to them in Xaml then you might need to use the DynamicResource markup extension instead of StaticResource, assuming you would know the keys at compile time. In WPF you can create resource dictionaries at runtime. In fact, that's what happens when you load a Xaml document, it's just that you don't see the equivalent C#.Prase
Thanks for the reply :) I'll be refering to them via FindResource, but I think this may save a ridiculous amount of processing in my app now so thanks :)Presentable
Something I hit: if you add your image resource to a resource dictionary, don't forget to refer to that image dictionary in the XAML for your component. Something like: <UserControl.Resources> <ResourceDictionary> <ResourceDictionary.MergedDictionaries> <ResourceDictionary Source="Dictionary1.xaml" /> </ResourceDictionary.MergedDictionaries> </ResourceDictionary> </UserControl.Resources>Sharilyn
I usually add Width="{Binding Source.PixelWidth, RelativeSource={RelativeSource Self}}" to the Image, as otherwise I often see images getting grotesquely scaled up for some reason (such as 16x16 icons stretched to something that looks like 200x200 pixels).Keto
I found that if the BitmapImage is declared in a referenced assembly's resourcedictionary, the UriSource needs to be a packURI for this to work. Otherwise, you will find that you can see the image in your xaml editor in VS but no image when debugging. Pack URIS: msdn.microsoft.com/en-au/library/aa970069(v=vs.100).aspxVenezuela
I noticed that if you define the resource in App.xaml resources it will work in the runtime but u can't see the feedback in the designer. I had to define the BitmapImage resource in another Resource Dictionary file and use MergedDictionary to see the feedback from the designer. Any explanation?Stubborn
P
207

I found to be the best practice of using images, videos, etc. is:

  • Change your files "Build action" to "Content". Be sure to check Copy to build directory.
    • Found on the "Right-Click" menu at the Solution Explorer window.
  • Image Source in the following format:
    • "/«YourAssemblyName»;component/«YourPath»/«YourImage.png»"

Example

<Image Source="/WPFApplication;component/Images/Start.png" />

Benefits:

  • Files are not embedded into the assembly.
    • The Resource Manager will raise some memory overflow problems with too many resources (at build time).
  • Can be called between assemblies.
Parlour answered 10/3, 2010 at 11:32 Comment(11)
This same approach works if you embed the resource in the assembly, but you have to set the "Build Action" to "Resource".Loewi
Works, thanks. One note for others: "component" is required "as is", "Images" is a relative path of png in the project. I.e. image that is placed in the root will be "<Image Source="/WPFApplication;component/Start.png" />"Eliason
An example of how to do this in C# would be nice. (That is not a valid URI so it can't be used when constructing a BitmapImage.)Unbodied
You also have to set "Copy to build directory" in the properties of the image. (this is not the exact translation, since I have German VS)Zirconium
So, how do you do it if the file is set to Embedded Resource? This doesn't seem to work. And I don't want to include the image in my project twice. (I'm already using it as an embedded resource.)Guayaquil
I seem to be getting a UriFormatException complaining about the impossibility to determine the format of the URI if I do it like that, unless I prepend pack://application:,,, to the path. As I am setting the resource URI from code-behind, I need to explicitly indicate UriKind.RelativeOrAbsolute to make the exception go away.Keto
Where and how does "component" come into the path. Is that part of some specification?Arturoartus
@Arturoartus The specification for Pack URIs is here msdn.microsoft.com/en-us/library/aa970069(v=vs.110).aspx ";component: specifies that the assembly being referred to is referenced from the local assembly."Hodeida
I think that @AshleyDavis comment doesn't apply anymore. If I mark my files as "Resources", they get embedded into the exe file.Chubby
My comment was from 10 years ago ;) Undoubtedly things have changed.Loewi
ditto I am confused over "component" being a necessary part of the path, or rather that it is actuall part of "your path". key however is specifying the source assembly, when image factoring is an implementation detail in question. thank you.Eurus
M
50

In code to load a resource in the executing assembly where my image Freq.png was in the folder Icons and defined as Resource:

this.Icon = new BitmapImage(new Uri(@"pack://application:,,,/" 
    + Assembly.GetExecutingAssembly().GetName().Name 
    + ";component/" 
    + "Icons/Freq.png", UriKind.Absolute)); 

I also made a function:

/// <summary>
/// Load a resource WPF-BitmapImage (png, bmp, ...) from embedded resource defined as 'Resource' not as 'Embedded resource'.
/// </summary>
/// <param name="pathInApplication">Path without starting slash</param>
/// <param name="assembly">Usually 'Assembly.GetExecutingAssembly()'. If not mentionned, I will use the calling assembly</param>
/// <returns></returns>
public static BitmapImage LoadBitmapFromResource(string pathInApplication, Assembly assembly = null)
{
    if (assembly == null)
    {
        assembly = Assembly.GetCallingAssembly();
    }

    if (pathInApplication[0] == '/')
    {
        pathInApplication = pathInApplication.Substring(1);
    }
    return new BitmapImage(new Uri(@"pack://application:,,,/" + assembly.GetName().Name + ";component/" + pathInApplication, UriKind.Absolute)); 
}

Usage (assumption you put the function in a ResourceHelper class):

this.Icon = ResourceHelper.LoadBitmapFromResource("Icons/Freq.png");

Note: see MSDN Pack URIs in WPF:
pack://application:,,,/ReferencedAssembly;component/Subfolder/ResourceFile.xaml

Masonry answered 16/3, 2012 at 13:15 Comment(8)
new Uri throws Invalid URI: Invalid port specified.Thornburg
Do you have the offending uri?Masonry
same uri as yours except that mine was running inside a winform hosted WPF. And the "pack" schema was not registered yet when I called new Uri.Thornburg
Oops... it's probably retated to winform hosted WPF. I'm sorry. I won't try to fix it because I think it is not a very common usage. Good luck!Masonry
In my case, using new Uri(@"pack://application:,,,/" + pathInApplication) also did the trick.Kline
@PeterLamberg, Did you corrected my code? It seems ok as it is now?Masonry
Note the important "component/" path prefix in the answer. It is IMHO slightly unexpected. My code was missing it and that's why it didn't work and it took me a while to see it.Folliculin
@EricOuellet Sorry, my previous comment had unclear wording. I didn't need to edit your anwer. I was just trying to emphasize the important components/ part as I completely failed to see it.Folliculin
A
49

Some people are asking about doing this in code and not getting an answer.

After spending many hours searching I found a very simple method, I found no example and so I share mine here which works with images. (mine was a .gif)

Summary:

It returns a BitmapFrame which ImageSource "destinations" seem to like.

Use:

doGetImageSourceFromResource ("[YourAssemblyNameHere]", "[YourResourceNameHere]");

Method:

static internal ImageSource doGetImageSourceFromResource(string psAssemblyName, string psResourceName)
{
    Uri oUri = new Uri("pack://application:,,,/" +psAssemblyName +";component/" +psResourceName, UriKind.RelativeOrAbsolute);
    return BitmapFrame.Create(oUri);
}

Learnings:

From my experiences the pack string is not the issue, check your streams and especially if reading it the first time has set the pointer to the end of the file and you need to re-set it to zero before reading again.

I hope this saves you the many hours I wish this piece had for me!

Af answered 13/2, 2011 at 16:49 Comment(0)
S
48

Yes, it is the right way.

You could use the image in the resource file just using the path:

<Image Source="..\Media\Image.png" />

You must set the build action of the image file to "Resource".

Swop answered 7/12, 2008 at 17:34 Comment(2)
Thanks for this. Is there a way to do something similar with an ImageSource, essentially loading the image once into a resource dictionary. I fear that this approach loads the image data multiple times in memory.Prase
This will be a mess when you need to refactor your code. You will have to manually change all the image references if your xaml document happens to change namespace. The method described by Drew Noakes is a lot smoother and maintable.Grimy
W
14

Full description how to use resources: WPF Application Resource, Content, and Data Files

And how to reference them, read "Pack URIs in WPF".

In short, there is even means to reference resources from referenced/referencing assemblies.

Writing answered 13/4, 2010 at 19:12 Comment(1)
The link appears to be live and well (though it says "This documentation is archived and is not being maintained.").Chromatic
F
8
  1. Visual Studio 2010 Professional SP1.
  2. .NET Framework 4 Client Profile.
  3. PNG image added as resource on project properties.
  4. New file in Resources folder automatically created.
  5. Build action set to resource.

This worked for me:

<BitmapImage x:Key="MyImageSource" UriSource="Resources/Image.png" />
Feudatory answered 23/8, 2011 at 9:16 Comment(0)
E
4

If you're using Blend, to make it extra easy and not have any trouble getting the correct path for the Source attribute, just drag and drop the image from the Project panel onto the designer.

Erotogenic answered 7/12, 2008 at 23:52 Comment(0)
C
2

Yes, it's the right way. You can use images in the Resource file using a path:

<StackPanel Orientation="Horizontal">
    <CheckBox  Content="{Binding Nname}" IsChecked="{Binding IsChecked}"/>
    <Image Source="E:\SWorking\SharePointSecurityApps\SharePointSecurityApps\SharePointSecurityApps.WPF\Images\sitepermission.png"/>
    <TextBlock Text="{Binding Path=Title}"></TextBlock>
</StackPanel>
Creaturely answered 24/4, 2018 at 10:47 Comment(0)
M
2

Building on the answer by Drew Noakes, here are the complete steps I followed to create a resource dictionary, add a BitmapImage resource to it, and reference the BitmapImage resource in a user control.

  1. Add an Images folder at the project root.
  2. Add MyImage.png under the Images folder.
  3. In the MyImage.png Properties window, set Build Action to Resource.
  4. Create a resource dictionary at the project root named MainResourceDictionary.xaml:
<ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
  <BitmapImage x:Key="MyImageSource" UriSource="Images/MyImage.png" />
</ResourceDictionary>
  1. Add a reference to the resource dictionary in the control:
<UserControl ...>
    <UserControl.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="MainResourceDictionary.xaml" />
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </UserControl.Resources>
    ...
  1. Reference the image resource in the control:
<UserControl ...>
    <UserControl.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="MainResourceDictionary.xaml" />
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </UserControl.Resources>
    ...
    <Image Source="{DynamicResource ResourceKey=ServiceLevel1Source}" />
    ...
Mediacy answered 31/5, 2022 at 21:30 Comment(0)
G
-4

The following worked and the images to be set is resources in properties:

    var bitmapSource = Imaging.CreateBitmapSourceFromHBitmap(MyProject.Properties.Resources.myImage.GetHbitmap(),
                                      IntPtr.Zero,
                                      Int32Rect.Empty,
                                      BitmapSizeOptions.FromEmptyOptions());
    MyButton.Background = new ImageBrush(bitmapSource);
img_username.Source = bitmapSource;
Gonyea answered 8/1, 2015 at 23:4 Comment(1)
No offence but this smells like bad practice.Intervocalic

© 2022 - 2024 — McMap. All rights reserved.