views:

155

answers:

2

Icons have been provided in the SDK for either "Light" or "Dark" depending on the theme set on the phone. On the application bar when the theme changes the icons automatically change with it. Also, when you press the button the image is reversed (so it's still visible) no matter which theme you're on. I can easily figure out how to change the icon based on the current theme. However what's no easily figured out is how to change the icon when the button is pressed.

To be more clear. Let's say I'm using the "Dark" theme. I create a button that uses the dark icon. When the button is pressed the background is white but the icon itself remains white and therefore is not visible. On the application bar the icon would become black which is of course visible against the white background.

Is this making sense? Anyone know how to fix this?

+3  A: 

Instead of using light and dark icons you can use a single icon as an OpacityMask. This is just an example, you may want to make it into a separate control to make setting the icon neater, you can have C# code for a ToggleButton if you want. Also this series might be of some interest.

<Style x:Key="IconButton" TargetType="Button">
        <Setter Property="Background" Value="Transparent"/>
        <Setter Property="BorderBrush" Value="{StaticResource PhoneForegroundBrush}"/>
        <Setter Property="Foreground" Value="{StaticResource PhoneForegroundBrush}"/>
        <Setter Property="BorderThickness" Value="{StaticResource PhoneBorderThickness}"/>
        <Setter Property="FontFamily" Value="{StaticResource PhoneFontFamilySemiBold}"/>
        <Setter Property="FontSize" Value="{StaticResource PhoneFontSizeMediumLarge}"/>
        <Setter Property="Padding" Value="10,3,10,5"/>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="Button">
                    <Grid Background="Transparent">
                        <VisualStateManager.VisualStateGroups>
                            <VisualStateGroup x:Name="CommonStates">
                                <VisualState x:Name="Normal"/>
                                <VisualState x:Name="MouseOver"/>
                                <VisualState x:Name="Pressed">
                                    <Storyboard>
                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Background" Storyboard.TargetName="ContentContainer">
                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource PhoneBackgroundBrush}"/>
                                        </ObjectAnimationUsingKeyFrames>
                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Background" Storyboard.TargetName="ButtonBackground">
                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource PhoneForegroundBrush}"/>
                                        </ObjectAnimationUsingKeyFrames>
                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="BorderBrush" Storyboard.TargetName="ButtonBackground">
                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource PhoneForegroundBrush}"/>
                                        </ObjectAnimationUsingKeyFrames>
                                    </Storyboard>
                                </VisualState>
                                <VisualState x:Name="Disabled">
                                    <Storyboard>
                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Background" Storyboard.TargetName="ContentContainer">
                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource PhoneDisabledBrush}"/>
                                        </ObjectAnimationUsingKeyFrames>
                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="BorderBrush" Storyboard.TargetName="ButtonBackground">
                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource PhoneDisabledBrush}"/>
                                        </ObjectAnimationUsingKeyFrames>
                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Background" Storyboard.TargetName="ButtonBackground">
                                            <DiscreteObjectKeyFrame KeyTime="0" Value="Transparent"/>
                                        </ObjectAnimationUsingKeyFrames>
                                    </Storyboard>
                                </VisualState>
                            </VisualStateGroup>
                        </VisualStateManager.VisualStateGroups>
                        <Border x:Name="ButtonBackground" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" CornerRadius="0" Margin="{StaticResource PhoneTouchTargetOverhang}">
                            <Grid x:Name="ContentContainer" OpacityMask="{TemplateBinding Content}" Background="{TemplateBinding Foreground}"/>
                        </Border>
                    </Grid>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

Usage

<Button Style="{StaticResource IconButton}" >
    <ImageBrush ImageSource="appbar.feature.search.rest.png" Stretch="None"/>
</Button>

Explanation: An OpacityMask uses the alpha value of a brush for the element, as it takes a brush you could use a gradient or ImageBrush. In this case the ContentContainer which is rectangular takes the shape of supplied image, as only the alpha channel is used it can be any colour you want. The ContentContainer uses the foreground colour which the style changes when its pressed, you can change the icon colour by changing the buttons foreground. This style is basically the default but instead of a ContentControl a Grid with an OpacityMask is used instead.

Normally you wouldn't put an ImageBrush directly in a button but this is done as a quick workaround for some of the binding limitations in Silverlight v3. Alternatively you could use a custom control with a Uri property for an Icon which updates a Brush property for the mask. The style would use the custom Brush property as the OpacityMask instead of Button.Content. As the OpacityMask only uses the alpha channel this method isn't suitable for colour images.

Kris
ill second this, opacity mask is the way to go, a bit confusing at first, but well worth understanding
Mark
I think I understand what your saying, but the example doesn't work. It just gives me an all white button.
Micah
I tested it with both light and dark themes, check the image is in your project and your adding an ImageBrush to the button not an image.
Kris
It was the ImageBrush that fixed it. It works, but I'm not sure why. can you give me a good explanation of what's going on? I have pretty advanced knowledge of WPF/Silverlight, but I've never used the OpacityMask before.
Micah
Glad you got it working. I added an explanation of how it works.
Kris
A: 

Actually - WP7 has some smarts around it and will automatically make "dark" versions of light coloured icons (and also handle button press properly) provided the following conditions are met:

  • The icon is 48x48
  • In .PNG format (haven't actually tried with .jpg but i doubt it'll work)
  • Where the icon is white and the background is transparent (might work with black also but your question seems to suggest otherwise)

I'm using one set of icons in a WP7 app, and it handles light/dark themes automatically - there used to be an AppBarIcon pack from Microsoft as well, but a quick google is just giving me broken links at the moment. Send me an email if you're desperate and I can flick them your way.

While the suggested approach with OpacityMask probably works, it sounds a lot more difficult than just using white on transparent 48x48 pngs. :) Let me know how you go!

Blakomen
The problem is that without manually doing the opacity mask as suggested it doesn't work. The only place it works is on the application bar.
Micah
Right - sorry, I misunderstood your question - I thought you were trying to get an appbar icon to go, not just an icon for general use in your app. Sorry for the confusion!
Blakomen
Just a note on this - I used the LIGHT themed SDK buttons in my App bar and my app failed on point 5.5 - with a Dark Theme the inside of the button was black. If you start with the DARK theme SDK buttons then it reverses the color correctly depending on what theme you are using... so moral of the story - use the DARK themed SDK buttons in your app bar, NOT the light ones...
Rodney