Custom button template in WPF

Archie picture Archie · Apr 29, 2010 · Viewed 62.9k times · Source

I want to create a simple button template with an image and text inside it. But I want to keep the System button's look and feel.

How do I create it, step by step?

P.S.: I have already tried it with CustomControl in WPF and BasedOn property.

Answer

jeffora picture jeffora · Apr 29, 2010

You can do this easily with a style and attached property:

<ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:ap="clr-namespace:MyProject.Namespace.Path.To.ButtonProperties">
    ...
    <Style x:Key="ImageButton" TargetType="Button">
        <Setter Property="ContentTemplate">
            <Setter.Value>
                <DataTemplate>
                    <StackPanel Orientation="Horizontal">
                        <Image Source="{Binding Path=(ap:ButtonProperties.Image), RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Button}}}"></Image>
                        <ContentPresenter Content="{Binding Path=Content, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Button}}}"></ContentPresenter>
                    </StackPanel>
                </DataTemplate>
            </Setter.Value>
        </Setter>
    </Style>
    ...
</ResourceDictionary>

and

public class ButtonProperties
{
    public static ImageSource GetImage(DependencyObject obj)
    {
        return (ImageSource)obj.GetValue(ImageProperty);
    }

    public static void SetImage(DependencyObject obj, ImageSource value)
    {
        obj.SetValue(ImageProperty, value);
    }

    public static readonly DependencyProperty ImageProperty =
        DependencyProperty.RegisterAttached("Image", typeof(ImageSource), typeof(ButtonProperties), new UIPropertyMetadata((ImageSource)null));
}

Then in markup:

<Button Style="{StaticResource ImageButton}" ap:ButtonProperties.Image="{StaticResource MyImage}" Content="Test">
</Button>

This example looks pretty hideous, but you can easily change the StackPanel to a Grid or something similar to constrain the image proportion. Using the ContentPresenter allows you to preserve the behaviour of a button allowing you to put any UIElement inside, and retaining Command support etc.