Days ago I needed a way to add an image and some other extensions to the standard button controls in WPF, I did a similar thing in the past in SIlverlight, but I had to rewrite the full control template.

I wanted to avoid this situation, so I started looking around and I found two very good post on the subject:

WPF Control Development - 3 Ways to build an ImageButton

Using Attached Properties to Create a WPF Image Button

Both those approach are good but they didn’t satisfied me in full: bad support for templating in the first case, and too much xaml to write in the second. So I merged both approach and came out with a solution that fulfills my needs:

  1. I created and inherited control
  2. I added some dependency properties to use it in xaml (that is specify the image)
  3. I used the ‘ContentTemplate’ and ‘DataTemplate’ features to define the style of the control (replacing the facto the standard content of the button.

Here’s the new button class:

public class ImageButton : Button
{
   static ImageButton()
   {
      DefaultStyleKeyProperty.OverrideMetadata(typeof(ImageButton), new FrameworkPropertyMetadata(typeof(ImageButton)));
   }
 
   public ImageButton()
   {
      this.Loaded += new RoutedEventHandler(ImageButton_Loaded);
   }
 
   /// <summary>
   /// this event is used to adapt the style in case the control is used in a toolbar
   /// </summary>
   /// <param name="sender"></param>
   /// <param name="e"></param>
   void ImageButton_Loaded(object sender, RoutedEventArgs e)
   {
      if (Style == null && this.Parent is ToolBar)
      {
         Style = (Style)FindResource(ToolBar.ButtonStyleKey);
      }
   }
   
   public ImageSource Image
   {
      get { return (ImageSource)GetValue(ImageProperty); }
      set { SetValue(ImageProperty, value); }
   }
 
   // Using a DependencyProperty as the backing store for Image.  This enables animation, styling, binding, etc...
   public static readonly DependencyProperty ImageProperty =
       DependencyProperty.Register("Image", typeof(ImageSource), typeof(ImageButton), new UIPropertyMetadata(null));
 
}

And here’s the template (to be placed in the generic.xaml file)

<Style TargetType="{x:Type Buttons:ImageButton}" BasedOn="{StaticResource {x:Type Button}}">
   <Setter Property="ContentTemplate">
      <Setter.Value>
         <DataTemplate>
            <StackPanel Orientation="Horizontal">
               <Image Source="{Binding Path=(Image),
                    RelativeSource={RelativeSource FindAncestor,
                    AncestorType={x:Type Buttons:ImageButton}}}" />
               <TextBlock
         Text="{TemplateBinding Content}"
         HorizontalAlignment="Center" />
            </StackPanel>
         </DataTemplate>
      </Setter.Value>
   </Setter>
</Style>

Here the trick is to use the ‘BasedOn’ attribute of the Style, In this way we can inherit from the standard control template and override only the attribute we want to change.

The usage of this class now satisfies me:

<Buttons:ImageButton Content="Save" IsEnabled="{Binding AllowEdit}" 
                     Click="BtnSave_Click" 
                     Image="/XXXX.Warehouse;component/Images/save-alt.png" />

A similar approach can be followed to customize any other WPF control.

Related Content