Create a round button control for Windows Phone 7

officeAs you might have seen in the Windows Phone 7 Office application there are small round buttons in the panorama. As you should never use the application bar in the panorama it’s a good way to get some navigation points into the panorama. There are a few versions of this button out, ranging from styling a button and up to complete controls. However the ones I have found have things I don’t like, mostly they work with black and white images or with paths for the icon.

So I started out with a few basic specifications, I wanted:

  • A simple control to use, no special content
  • Only mage for the button (white with alpha channel)
  • To be able to easily add content next to the button
  • Ability to re-skin the button

The goal was to be able to define a button with just a simple:

For those that don’t want to find out about my journey towards the RoundButton control there is a zip with the code and a sample project at the very bottom of the post…

When I first started out with the basic design the approach was the same as for others out there, two images that was swapped on light theme and shifted on pressed state. But I didn’t like it, I wanted the control to handle the image inversion stuff so my next thought was to create a new image with Writable bitmap and do some pixel magic, but before I got that far I stumbled on a blog post on displaying an image in the accent color. This was brilliant, using the image as a opacity mask, I could have whatever color I liked. So the core of the control got to be something like this:


  
    
  

The code

On to the control design, first of was to create the code behind for the button, creating the attached properties. This code ended up looking like this:

public class RoundButton : Button
{
    private object _layoutRoot;
    public static readonly DependencyProperty ImageProperty  = DependencyProperty.Register("Image", typeof(ImageSource), typeof(RoundButton), null);

    public RoundButton() : base()
    {
        DefaultStyleKey = typeof(RoundButton);
    }


    [Description("The image displayed by the button in dark theme (and in normal mode)"), Category("Common Properties")]
    public ImageSource Image
    {
        get { return (ImageSource)GetValue(ImageProperty); }
        set { SetValue(ImageProperty, value); }
     }

    public override void OnApplyTemplate()
    {
        _layoutRoot = GetTemplateChild( "LayoutRoot" ) as Border;
        Debug.Assert( _layoutRoot != null, "LayoutRoot is null" );
        base.OnApplyTemplate();
    }
}

Hurdles and solutions

So the template stuff should be easy now right? Just add the Image to ImageSource for the Brush like this:


Wrong! Turns out you can’t bind to an ImageSource in an ImageBrush, because ImageBrush don’t inherit from FrameworkElement, as BMiloshevska pointed out. Argh, so close. Back to the drawing board until Derek Lakin pointed me at an article about using a converter as a workaround. Here is the converter stuff:

public class ImageBrushConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        BitmapImage image = (BitmapImage)value;
        ImageBrush imageBrush = new ImageBrush();
        if (image != null)
        {
            imageBrush.ImageSource = image;
        }
        return imageBrush;
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}


Getting closer, but still there are a few hurdles left as you will see. Now it seems you can bind the ImageBrush to the DependecyProperty with a Converter, like this:


Wrong again! You can’t use a converter on a TemplateBinding, sigh. Then a new angle: The DataContext. You can TemplateBind the image to the DataContext and then use a normal binding (with converter) for the brush. Success! The binding ended up looking like this:

The template

So now it was the last part left, creating the template style with states for normal, pressed and disabled. The last one is a bonus, with the image approach you would now need three images, white, black and grey, no you can use whatever you like. The template was placed in Themes/generic.xaml and looks in part like this (the full xaml is included in the zip):

There, finally I reached my goal!

Improvements

So am I done now? Yes and no. The functionality is in place and the first four goals reached but there is more stuff I like to do:

  • I want to add some optional animation, for example the push behavior from the AppBar and the tilt effect (actually you can use the tilt effect that Peter Torr blogged about, as the control is based on a button)
  • I think there is a workaround to the Converter, I would like to eliminate that part.
  • Also create a round toggle button with two images (for example play/pause media button)

If anyone happen to have a solution or idea around those issues I would be happy to know.


The zip includes the control and a test project to try it out.

This Post Has 6 Comments

    1. Yes I know that, and it’s one way to solve it (I linked to it in the beginning as one way to solve it). I wanted to take it a bit further. My main problem with that solution is that:

      – It needs two images (or three if you want to disable it)
      – I want to easily add text/content to the righ (as in the WP7 office version of the button)
      – I wanted it skinnable, if you like to change size or content placement it’s easy to do

      Hmm, maybe it could be interesting to add this solution into the coding4fun project?

  1. Hello, I am very new to WP7 and i tried to implement the rounded button using your code which worked. But now i realised that some images are not loaded even though they’re png format. Any ideas why this could be happening?

  2. Thank you very much :D
    What a nice solution.

Leave a Reply

Close Menu