Better Theme-Aware Icon Buttons in Windows Phone 7

By | July 25, 2010
Claus points out in a comment below that you could also use the vector-formats that Microsoft provides with their icon files. Tidy solution. My opacity mask approach will still be useful where you have a partly-transparent raster image that you want to automatically render with the foreground color, regardless of the phone theme.

A few weeks back I posted a fairly hacky solution to have theme aware icon buttons (like the ones on the application bar). At the time, I knew there must be a better way. I’ve found it now.

The trick is to use the icon image as an opacity mask against a rectangle using the system foreground color. This way you don’t have to change the template at all, unless you actually want to change the image on the button.

So here’s the control template for a “play” button that will automatically change from black to white depending on theme. Note that we need two superimposed rectangles to cater for the circle and the icon. If you made composite png images, you’d only need one rectangle:

[code lang=”xml” wrap=”false”]
<ControlTemplate x:Key="PlayButton" TargetType="Button">
<Canvas Width="48" Height="48">
<Rectangle Fill="{StaticResource PhoneForegroundBrush}" Width="48" Height="48" >
<Rectangle.OpacityMask>
<ImageBrush ImageSource="/Content/Images/AppBarIcons/dark/appbar.transport.play.rest.png" />
</Rectangle.OpacityMask>
</Rectangle>
<Rectangle Fill="{StaticResource PhoneForegroundBrush}" Width="48" Height="48">
<Rectangle.OpacityMask>
<ImageBrush ImageSource="/Content/Images/AppBarIcons/dark/appbar.basecircle.rest.png" />
</Rectangle.OpacityMask>
</Rectangle>
</Canvas>
</ControlTemplate>
[/code]

And here’s how you’d use it:

[code lang=”xml”]
<Button x:Name="PlayButton" Click="Play_Click" Template="{StaticResource PlayButton}" Width="48" Height="48" Margin="7" />
[/code]

MUCH more elegant than the previous solution.

9 thoughts on “Better Theme-Aware Icon Buttons in Windows Phone 7

  1. Claus Jørgensen

    As I mentioned on Twitter, I would go with the vector graphics instead, as it’s easier to theme, and more lightweight

    Your example, modified:

    [code]
    <ControlTemplate x:Key="PlayButton" TargetType="Button">
    <Canvas Width="48" Height="48">
    <Path Data="F1M84.127,709.4629L70.558,719.8039L70.558,699.2159z" Fill="{StaticResource PhoneForegroundBrush}" Canvas.Top="14" Canvas.Left="17" Stretch="Fill" Height="20" Width="14" />
    <Ellipse Stroke="{StaticResource PhoneForegroundBrush}" StrokeThickness="1" Width="48" Height="48" />
    </Canvas>
    </ControlTemplate>

    <Button x:Name="PlayButton" Template="{StaticResource PlayButton}" Width="48" Height="48" />
    [/code]

    Reply
    1. Ben Post author

      Very nice, tidy solution. I’ll make sure to get the vector graphics for our custom icons from the designers!

      Reply
      1. Claus Jørgensen

        Expression Blend supports import of .ai files from Adobe Illustrator, but I heard Inkscape also supports proper XAML support of vector graphics by now.

        Reply
  2. Keff

    WP7 UX guidelines state that the circle will be added on top of the icon automatically by OS, so the icon shouldn’t contain it. Opart from that, thanks for a cool idea! :)

    Reply
    1. Ben Post author

      Yep that’s correct for application bar icons. But this is a follow-on from my original article where I’m using similar buttons within the main application frame. In this cas WP7 doesn’t automatically add the circle nor do the colour inversion for you.

      Reply
  3. SandRock

    Hey, your tip is really cool for Buttons. But how would you proceed with buttons of type ApplicationBarIconButton?

    Reply
  4. Pingback: Windows Phone 7 ???? | DanceCoder

  5. DanH

    I know it’s an old thread, but any idea how you’d achieve the same effect from within the code-behind?

    Tried:

    Dim rect As New Rectangle With {
    .Width = 32,
    .Height = 32,
    .Fill = Resources(“{StaticResource PhoneForegroundBrush}”),
    .OpacityMask = New ImageBrush With {.ImageSource = New BitmapImage(New Uri(“/Images/appbar.check.rest.png”, UriKind.Relative))}
    }

    No joy though.

    Reply

Leave a Reply

Your email address will not be published. Required fields are marked *