Pablo Picasso
"Computers are useless. They can only give you answers."
My Latest Tweets
Tags

This will be shown to users with no Flash or Javascript.
Calendar
<<  September 2010  >>
MoTuWeThFrSaSu
303112345
6789101112
13141516171819
20212223242526
27282930123
45678910
Disclaimer

The opinions expressed herein are my own personal opinions and do not represent my employer's view in anyway.

Sign in

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.

With January almost over, my new project at work is now well under way. The application that I’m working on is essentially a WPF smart client that will be built on top of Composite WPF (aka PRISM) and follow the well-established Model View ViewModel (MVVM) pattern. As it has been almost a year since I last programmed in WPF, I had to brush up quite a bit on my XAML, PRISM, Unity and MVVM. Here is the list of resources which I have found particularly useful to help me catch up with it all.

MVVM Pattern

Prism

Starter Kits and Sample Apps

Frameworks

Note: Jeremy Alles’ quick tour of existing MVVM frameworks and feature comparison matrix is well worth checking out before delving into any of the frameworks above.