WPF: yet another way to customize buttons controls (and controls in general)

Print Content | More

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.

Button, Control, Customize, Image, Wpf

4 comments

Related Post

  1. #1 da XAML Templates - Saturday March 2010 alle 01:15

    Hi, at http://www.xamltemplates.net you can find themes for all the WPF and Silverlight controls.

  2. #2 da Eric Ouellet - Saturday March 2010 alle 01:15

    Grrrreat !!! thanks !!!

  3. #3 da Guardian - Saturday March 2010 alle 01:15

    From the template above, on the StackPanel tag, remove the Orientation="Horizontal" attribute.

  4. #4 da Nelson Rivers - Saturday March 2010 alle 01:15

    How can you create a button that contains both an icon image and text, where the text sits at bottom, and the icon image (48x48) sits right above it ?

All fields are required and you must provide valid data in order to be able to comment on this post.


(will not be published)
(es: http://www.mysite.com)