WPF Control Development – 3 Ways to build an ImageButton


During a WPF workshop last week I was asked to do an ImageButton control. *Huh* I thought, this is going to be an easy task (as probably most of you think right now).

1st Way: Use Buttons Content Property directly:

And it was easy: Thanks to the concept of ContentControls I easily assigned the Button.Content property (in the sample below left out because it’s the default property) a Stackpanel arranging the Image and the Text next to each other.

...
<
Button VerticalAlignment="Top" HorizontalAlignment="Left" Click="Button_Click" Background="Blue"> <StackPanel Orientation="Horizontal" Margin="10"> <Image Source="calendar.png" Width="16" Height="16" Margin="0,0,10,0"/> <TextBlock>Calendar</TextBlock> </StackPanel> </Button>
...

Bravely I showed them these simple 6 lines – I really enjoyed it! – They didn’t 🙁

"We don’t wanna use this 6 lines everywhere just to get an image button!", they complained..

So I showed them how to refactor a custom control out of this. First of all I tried inheriting from Button (which is the hard way for this case):

2nd Way: Create an inherited control:

I created a new control, inherited it from Button and created the visual tree for the content property out of C# code in the constructor:

    public class ImageButton : Button
    {
        Image _image = null;
        TextBlock _textBlock = null;

        public ImageButton()
        {
            StackPanel panel = new StackPanel();
            panel.Orientation = Orientation.Horizontal;

            panel.Margin = new System.Windows.Thickness(10);
            
            _image = new Image();
            _image.Margin = new System.Windows.Thickness(0, 0, 10, 0);
            panel.Children.Add(_image);

            _textBlock = new TextBlock();
            panel.Children.Add(_textBlock);

            this.Content = panel;
        }

        // Properties
    }

This does exactly the same like the 1st sample, but encapsulates everything in an own class. I also added properties, to set the _textBlock.Text and _image.Source for the button.

The advantage is you can now use the ImageButton directly out of XAML in one line:

Specify a custom Xml-Namespace prefix:

<Window ... xmlns:my="clr-namespace:ImageButtonDemo">

and use the button in your XAML

<my:ImageButton Image="calendar.png" Text="Calendar" />

This is quite simple to use, but the creation of the Content via C# is more complex than it needed to be.

So I decided to do a 3rd – mixed way of the first ones:

3rd Way: Create a UserControl:

In contrary to inherited controls, UserControls are composite controls, which can consist of many subcontrols. UserControl inherits like Button or Window from ContentControl, so you can think of it as an embeddable window area. As with window, UserControl consists of both, a XAML file as well as a code behind file.

So what I did is placing a single button into the UserControl and set its content as before:

<UserControl Name="UC" ...>
    <Grid>
        <Button>
            <StackPanel Orientation="Horizontal">
                <Image .../>
                <TextBlock .../>
            </StackPanel>
        </Button>
    </Grid>
</UserControl>

For setting the image’s and the textblock’s values I could again have used simple properties, but this time I decided to use databinding:

I made up dependency properties, to externally access the imagebutton’s properties:

public string Text

{

  get { return (string)GetValue(TextProperty); }

  set { SetValue(TextProperty, value); }

}

public static readonly DependencyProperty TextProperty =

  DependencyProperty.Register("Text", typeof(string), typeof(ImageButton2), new UIPropertyMetadata(""));

public ImageSource Image

{

   get { return (ImageSource)GetValue(ImageProperty); }

   set { SetValue(ImageProperty, value); }

}



public static readonly DependencyProperty ImageProperty =

   DependencyProperty.Register("Image", typeof(ImageSource), typeof(ImageButton2), new UIPropertyMetadata(null));

A tipp: Dependency Properties as shown above can be easily created with a code snippet by typing propd <tab><tab> in Visual Studio.

What I did then, is bind these dependency properties to the XAML object’s properties:

<Image Source="{Binding ElementName=UC, Path=Image}"/>
<TextBlock Text="{Binding ElementName=UC, Path=Text}" />

That’s it!

 

In my opinion the last method is a good combination: Everything encapsulated in an own object, but having the benefits of XAML as well.

And here is the ImageButton:

image

For the entire code download the attached project!

UPDATE: Another fine approach using attached properties (in a more decorator like way) can be found Philipp Sumi’s blog.

Knom.WPF.ImageButton.zip


Comments (9)

  1. Lucho says:

    Excellent summary of the different approaches you can use to create the imagebutton

    Thanks

  2. R says:

    The 3rd way was exactly what I was looking for.

    Thank you for your explanation!

  3. Philipp says:

    I used to do take an alternative route through attached properties. I think they are a very flexible alternative, that nicely complements custom controls. In case somebody’s interested, I published a short tutorial on my blog:

    http://www.hardcodet.net/2009/01/create-wpf-image-button-through-attached-properties

    WPF never ceases to amaze me with its flexibility 🙂

    Cheers,

    Philipp

  4. mobydisk says:

    The problem with the 3rd way is that you don’t get Click events on your button.

  5. harsh says:

    Could you tell me how to set image to the dynamically created button

  6. Cleric Stormgate says:

    As mobydisk said, with the 3rd way you don’t get click events… if you don’t do some extra work 😉

    1) Declare an event for the control with this line before the constructor

    public event RoutedEventHandler Click;

    2) Handle click over the button:

    In XALM: <Button Name="button" Click="button_Click">

    3) In code, complete the button_Click function this way:

    private void button_Click(object sender, RoutedEventArgs e)

    {

    if (null != Click)

    Click(sender, e);

    }

    And you’re done! If anyone can improve this code, please do so!

  7. this is so useful, thank you so much 🙂

  8. A very nice sample, but how can I associate a Command to the UserControl (the 3rd way)

    any help?

    thx 🙂

  9. A very naice sample, but how I can associate a Command to the UserControl (the 3rd way..)

    any help?

    thx 🙂