Adding an image in WPF is very straight forward. All you need to do is declare an Image element in XAML and specify the path of the image in the Source attribute:

<Image Source="about.png" />

But rather than deploying all your image files with your application, you might want to embed them in the assembly as resources:

Image resource build action. Once you’ve done that, you can start referring to images using either an absolute or relative URI:

<Image Source="pack://application:,,,/
Tbf.Presentation.Common;component/UI/Images/menu/about.png" />

or

<Image Source="Tbf.Presentation.Common;component/UI/Images/menu/about.png" />

Here Tbf.Presentation.Common is the assembly containing the compiled resource and UI/Images/menu is the project folder where the image is located:

Image folder location.

But this is really prone to errors and can quickly become a maintenance headache if you rename or move files around. Instead wouldn’t it be better to have all the pack URIs into one place? The way to do this is to add a new resource dictionary, say ImageResources.xaml, to your project.

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:Images="clr-namespace:Tbf.Presentation.Common.UI.Images">

    <!-- Menu -->
  <ImageSource x:Key="about_menu">
	Tbf.Presentation.Common;component/UI/Images/menu/about.png
  </ImageSource> 

</ResourceDictionary>

Then simply merge it into your App.xaml file or any other application-wide resource file…

<Application x:Class="Tbf.Presentation.Shell.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             Startup="Application_Startup"
             Activated="Application_Activated"
             Deactivated="Application_Deactivated"
             DispatcherUnhandledException="Application_DispatcherUnhandledException"
             Exit="Application_Exit">
    <Application.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>

                <ResourceDictionary Source="pack://application:,,,/
                       Tbf.Presentation.Common;component/Infrastructure
                       /Resources/ImageResources.xaml" />

            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Application.Resources>
</Application>

… to make images available as dynamic resources across the whole application:

<Image Source="{DynamicResource about_menu}" />

In terms of maintainability however this is still not satisfactory as there is no design or compile time checking to ensure that the key is correct. To improve on this, we can introduce a new ImageLibrary class in the project to help us specify the image resource key in XAML:

namespace Tbf.Presentation.Common.UI.Images
{
    #region Using Directives 

    using System.Windows; 

    #endregion 

    public class ImageLibrary
    {
        public static ComponentResourceKey AboutMenu
        {
            get
            {
                return new ComponentResourceKey(typeof(ImageLibrary), "about_menu");
            }
        } 
    }
} 
 

Next we have to go back to the ImageResources.xaml file and modify it to make use of the new helper class:

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:Images="clr-namespace:Tbf.Presentation.Common.UI.Images">

    <!-- Menu -->
    <ImageSource 
x:Key="{ComponentResourceKey TypeInTargetAssembly={x:Type Images:ImageLibrary}, ResourceId=about_menu}">
Tbf.Presentation.Common;component/UI/Images/menu/about.png
    </ImageSource> 

</ResourceDictionary> 

Et voilà! We have the support of intellisense for selecting images:Component key and Intellisense.A big improvement then? Yes but not quite perfect yet as now we have to define the key in two places: the ImageResources.xaml file and the ImageLibrary.cs file. To reduce further the amount of work we need to do, we can create a T4 template to automatically generate the ImageLibrary component keys:

1. Add a new item ImageLibrary.tt item in your project.

2. Copy and paste the code below (making the necessary adjustments for the namespace and ImageResources.xaml file path).

<#@ template debug="false" hostspecific="false" language="C#" #>
<#@ output extension=".cs" #>
<#@ assembly name="System.Core" #>
<#@ import namespace="System" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ import namespace="System.IO" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Text.RegularExpressions" #>
<#@ import namespace="System.Linq" #> 

namespace Tbf.Presentation.Common.UI.Images
{
    #region Using Directives 

    using System.Windows; 

    #endregion 

    public static class ImageLibrary
    {
<#
foreach (string key in this.GetComponentResourceKeys())
{
    string name = this.GetPropertyname(key);
#> 

        public static ComponentResourceKey <#= name #>
        {
            get
            {
                return new ComponentResourceKey(typeof(ImageLibrary), "<#= key #>");
            }
        }
<#
}
#>
    }
}<#+ 

public List<string> GetComponentResourceKeys()
{
    // Get the image resources file
    string file = @"<PATH>\ImageResources.xaml"; 

    // Read the image resources file
    string content; 

    using(System.IO.StreamReader sr = new System.IO.StreamReader(file))
    {
        content = sr.ReadToEnd();
    } 

    // Extract the keys
    Regex regex = new Regex(
                    "(?<=ResourceId=).*(?=})",
                    RegexOptions.CultureInvariant
                    | RegexOptions.IgnorePatternWhitespace
                    | RegexOptions.Compiled); 

    List<string> results = new List<string>();
    MatchCollection matches = regex.Matches(content); 

    for(int i = 0; i < matches.Count; i++)
    {
        results.Add(matches[i].Value);
    } 

    return results;
} 

public string GetPropertyname(string text)
{
    text = Regex.Replace(text, @"[\W-[\s]]", string.Empty);
    string[] words = text.Split('_', ' '); 

    for (int i = 0; i < words.Length; i++)
    {
        if (words[i].Length > 0)
        {
            string word = words[i];
            char firstLetter = char.ToUpper(word[0]);
            words[i] = firstLetter + word.Substring(1).ToLower();
        }
    } 

    return string.Join(string.Empty, words);
}
#> 

 

3. Run the TextTemplatingFileGenerator tool on the newly created .tt file.

Run TextTemplatingFileGenerator tool.

All done. The image library class is auto-generated and you can now set images in XAML knowing that any errors will be picked up either at design or compile time.