Home > WPF Controls > Using Attached Properties to Create a WPF Image Button

Using Attached Properties to Create a WPF Image Button

January 7th, 2009

Today I came across a nice blog post that described how to create a WPF image button with three different approaches. I’ve always taken an alternative route using attached properties, so here’s a short tutorial that complement’s the techniques outlined on Max’ Blog:

 

 image

 

Basically, the work to get there consists of two parts:

  • Declare an attached property that gets an image source.
  • Create a style (or multiple styles) that makes use of the attached property in order to display the image.

 

Attached Property

There’s nothing special here – I just declared an attached property named Image, which is of type ImageSource.

 

using System.Windows;
using System.Windows.Media;

namespace Hardcodet.Wpf.Util
{
  public class EyeCandy
  {
    #region Image dependency property

    /// <summary>
    /// An attached dependency property which provides an
    /// <see cref="ImageSource" /> for arbitrary WPF elements.
    /// </summary>
    public static readonly DependencyProperty ImageProperty;

    /// <summary>
    /// Gets the <see cref="ImageProperty"/> for a given
    /// <see cref="DependencyObject"/>, which provides an
    /// <see cref="ImageSource" /> for arbitrary WPF elements.
    /// </summary>
    public static ImageSource GetImage(DependencyObject obj)
    {
      return (ImageSource) obj.GetValue(ImageProperty);
    }

    /// <summary>
    /// Sets the attached <see cref="ImageProperty"/> for a given
    /// <see cref="DependencyObject"/>, which provides an
    /// <see cref="ImageSource" /> for arbitrary WPF elements.
    /// </summary>
    public static void SetImage(DependencyObject obj, ImageSource value)
    {
      obj.SetValue(ImageProperty, value);
    }

    #endregion

    static EyeCandy()
    {
      //register attached dependency property
      var metadata = new FrameworkPropertyMetadata((ImageSource) null);
      ImageProperty = DependencyProperty.RegisterAttached("Image",
                                                          typeof (ImageSource),
                                                          typeof (EyeCandy), metadata);
    }
  }
}

 

Property Declaration

Once this is done, you can attach the property to arbitrary items. I want to enhance a standard WPF button, so my XAML looks like this:

<Window
  x:Class="Hardcodet.Wpf.Util.Window1"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:local="clr-namespace:Hardcodet.Wpf.Util">
  <Grid>
    
    <!-- declare a button with an attached image -->
    <Button
      Content="OK"
      local:EyeCandy.Image="Ok.png" />
    
  </Grid>
</Window>

 

Providing a Button Style

However, just setting the attached property doesn’t change anything at all. This is no surprise: After all, the button does not know what to do with the attached image. Yet 🙂

This is were styles come into play. Here’s the button style I used for this sample:

 

<!--  A button style that displays an attached image -->
<Style
  x:Key="ImageButton"
  TargetType="{x:Type Button}">
  <Setter
    Property="HorizontalContentAlignment"
    Value="Stretch" />
  <Setter
    Property="ContentTemplate">
    <Setter.Value>
      <DataTemplate>
        <Grid>
          <Image
            Source="{Binding Path=(local:EyeCandy.Image),
                     RelativeSource={RelativeSource FindAncestor,
                       AncestorType={x:Type Button}}}"
            HorizontalAlignment="Left"
            Margin="8,0,0,0"
            Height="16"
            Width="16" />
          <TextBlock
            Text="{TemplateBinding Content}"
            HorizontalAlignment="Center" />
        </Grid>
      </DataTemplate>
    </Setter.Value>
  </Setter>
</Style>

 

A I explicitly named the style, the last step is to assign the style to the button:

<!-- declare a button with an attached image -->
<Button
  Content="OK"
  local:EyeCandy.Image="Ok.png"
  Style="{DynamicResource ImageButton}"
  />

 

Note that you are not limited to the standard WPF button class. Once the attached property is declared, you can attach an image to whatever control you like. All you have to do is just writing another style. You gotta love WPF 🙂

 

Blend Design Time Support

Blend fully supports attached properties. However, it might take one or two restarts.

image

 

Conclusion

User Controls are great, especially if want to create composite controls and provide multiple binding points. However, for simple customization, I still prefer the attached property approach over writing custom controls:

  • Apart from the property declaration, everything else is pure XAML.
  • I can style whatever control I like: Commercial 3rd party controls, tab headers, borders etc. without having to create new controls or even write code. All I have to do is create a new style.
  • Its fully up to the designer how to style controls one the properties have been declared. And I can even write multiple styles for the same control type in order to provide a different look and feel for a given control.

 

Sample Project: imagebuttons.zip

kick it on DotNetKicks.com


Author: Categories: WPF Controls Tags: ,
  1. January 8th, 2009 at 12:02 | #1

    Hi!
    That’s a nice approach as well!
    It’s a more decoration like approach!
    I’ve been using attached properties for other things as well (like input prevention, etc..) I’ll write a post about it soon on http://blogs.msdn.com/knom/!

    br,
    max.

  2. January 8th, 2009 at 12:20 | #2

    Hi Max
    Glad you like it! I think all approaches have their rightful place – but attached properties definitely make a nice addition to the toolbox.

    Regarding your post about input prevention – sounds like this could nicely complement validation techniques. Looking forward to it 🙂

  3. Andy Peters
    March 21st, 2009 at 11:05 | #3

    Good article.

    I’m trying to create your buttons dynamically but I am having trouble adding the image.

    local:EyeCandy.Image=”Ok.png”

    … in the code behind what would I use to do the above?

    Regards,
    Andy.

  4. March 21st, 2009 at 11:42 | #4

    Andy,

    Keep in mind that the Image property is an attached property. Have a look here (there’s a section about programmatically setting attached properties):
    http://msdn.microsoft.com/en-us/library/ms749011.aspx

    Cheers,
    Philipp

  5. Andy Peters
    March 21st, 2009 at 13:08 | #5

    Finally got it working with this:-

    Uri src = new Uri(“cancel.png”, UriKind.Relative);
    BitmapImage img = new BitmapImage(src);
    b.SetValue(EyeCandy.ImageProperty, img);

    Regards,
    Andy.

  6. c_manboy
    May 28th, 2009 at 22:20 | #6

    I’m getting an “Object not set to an instance of an object.” error when I place the class in a separate project. If I place it in the same project as the button, it works fine. I’m sure its something easy, but I’m not seeing it.

    Any suggestions?

  7. BigOldSofty
    June 30th, 2009 at 01:14 | #7

    I have seen so many methods of doing Image buttons now I’ve lost count. Unfortunately none of them address my problem. The app I’m working on HAS to be dynamic in many respects and a lot of the XAML code is generated on-the-fly at runtime and streamed into a panel or grid etc. What I have found in this case is that the stream reading fails either at the image or the namespace reference. Is there a way around this? I guess this is another instance of trying to use a technology for something it was not intended to do.

  8. Robert Gaut
    September 26th, 2009 at 02:32 | #8

    I really like this. Thanks for sharing it! Have you tried using it with triggers yet? I created a style using BasedOn=”{StaticResource ImageButton}” and created a trigger using Property=”IsMouseOver” and a Value=”true”. In the Setter I set the Property=”local:EyeCandy.Image” to a Value=”Ok_hover.png”. I can’t get the ImageSource to change to the new image. If you have any thoughts on this, I would greatly appreciate hearing them.

  9. March 10th, 2010 at 08:46 | #9

    Can this be implemented in VB.Net as I am not used to C# syntax.

    the EyeCandy class declarations are not found in VB.net, Please help on this.

  10. March 10th, 2010 at 09:32 | #10

    This should work regardless the implementation language (after all, after compilation, it’s just IL), and you can easily link to the compiled C# class. I assume it has to do with the reference, but given I don’t even have the VB parts on my system, I won’t be of much help I’m afraid.

  11. rlcrews
    July 22nd, 2010 at 17:14 | #11

    I’ve tried to implement this in Silverlight 4.0 but I cannot seem to get the image to render in button. I can path to the image and I receive no compile errors however the image does not appear. I did notice that SL does not support FrameworkPropertymetda so I used Property meta data instead. Is this same approach possible in Silverlight or should I look to another approach?

  12. Achilles
    January 3rd, 2011 at 19:48 | #12

    However you’ve wrote an abstract article and it is not intended to be read by real novices like me, I could manage to find my way throughout your sample codes 🙂

    Thanks.
    ——————
    To other Readers:
    1. Don’t forget to add xmlns:local=”clr-namespace:Hardcodet.Wpf.Util;assembly=Hardcodet[This is an example]”
    2. Use this Image tag in the template:

    3. Expression Blend 4 does not recognize this attached property, don’t waste your time on it; TYPE the address! 😀

  13. January 21st, 2011 at 19:11 | #13

    Thank you so much for this useful information!
    Bookmarked already 😀

  14. Nick
    May 9th, 2011 at 23:42 | #14

    Hi Phil

    If I change the image source from:
    RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Button}} TO RelativeSource={RelativeSource Mode=TemplatedParent}} then it doesnt work, but I wonder why not. Isnt button the parent?

    Cheers
    Nick

  15. Lijo
    May 30th, 2011 at 03:46 | #15

    Hi Philipp Sumi,

    Can you please share Silverlight version code for this?

  16. Mark
    July 13th, 2011 at 01:29 | #16

    1. Why isn’t that built-in???
    2. Nobody tackles the disabled state! The image will be unchanged while normally an image button greys the image.
    3. Why isn’t that built-in???

  17. July 16th, 2011 at 00:40 | #17

    @Mark
    With regards to 2: You can just change the Opacity is the control is disabled, which provides you with a similar effect.

  18. Hila
    November 29th, 2011 at 22:51 | #18

    Hi,
    Thank you for the good article
    I would like to ask what is the license of this article.
    under which terms we can use it? Are you placing it in the public domain? If not do you want to release under some license?
    thansk 🙂

  19. Greg10
    December 7th, 2011 at 19:03 | #19
  20. Maria
    May 13th, 2012 at 16:28 | #20

    Hi,
    Thank you very much for providing this solution. I tried it out and it worked for me, but I have one question:
    If I have a resources file with the image and instead of using the path to the image hardcoded, I want to refer to the key resource, how should I proceed?

    I mean instead of

    I tried something like this, but it’s not working…

    Any help would be appreciated 🙂

  21. Glenn
    July 11th, 2013 at 17:00 | #21

    Great example: I quickly repeated this (that’s the real end goal – right?) for Labels and Radio Buttons by just adding another style.

    Glenn

  22. Rahul
    December 1st, 2014 at 13:58 | #22

    Hi Philipp,

    It’s all fine and generic approach.

    If we define 100+ attached properties in a single class to use n-number of properties on X control, m-number of properties on Y control and so on..

    Will this approach be bad as this could make extra initialization of properties even when I am not using that attached property on that control with other attached properties of same attached property wrapper class?

    As I think, I should define particular set of attached properties in different classes.

    Please suggest?

  23. Scott Stafford
    April 24th, 2015 at 20:12 | #23

    Binding the Content to the TextBlock.Text property fails if the Content is not a string (like if it’s a with ‘s in it). I replaced the:

    with:

    and that allowed me to use more complex Button Contents.

  1. January 8th, 2009 at 03:02 | #1
  2. January 8th, 2009 at 12:07 | #2
  3. August 28th, 2009 at 11:07 | #3