Creating theme friendly UI in WP7 using OpacityMask
published on: 2/22/2011 | Views: N/A | Tags: UI Styling
by WindowsPhoneGeek
In this article I am going to talk about Opacity Mask in Windows Phone 7. Basically Opacity masks enable you to make portions of an element either transparent or partially transparent. To create an opacity mask, you apply a Brush to the OpacityMask property of an element or Visual(Every UIElement exposes a property called OpacityMask).The brush is mapped to the element or visual, and the opacity value of each brush pixel is used to determine the resulting opacity of each corresponding pixel of the element.
When writing wp7 applications it is a common task to make sure your app looks consistent in both dark and like themes (this is one of the Windows Phone 7 Application Certification Requirements). In most apps developers use icons, images , image buttons etc. So the question is how can we use the same icons/images in both themes so that they stay consistent with the theme colors? Basically the easiest (but definitely not the best) way is to use the well known approach with two icons, one for dark and another for light themes. A better solution could be to use a single icon/image and OpacityMask.
According to the latest version of the Windows Phone 7 Application Certification Requirements (Here's a direct link to the PDF file):
5.5 Content Validation
The application content (e.g. text, visual elements) must be visible and legible regardless of the phone theme. For example, if the phone theme changes from black background to white background, the text and visual elements of your application must be visible or legible.
So testing your app in light/dark theme is vital and if you miss this step your app can even be rejected.
Dark and light theme testing
A common mistake that most developer do is to forget to test their apps in dark/light themes. By default the emulator displays the dark theme, so it's easy to bypass theme switching during the testing phase. Failure to test theme switching will result in elements melding or disappearing within the background. Many elements in the UI support system-wide theming, however, some do not, specifically custom built controls and images/icons. Testing your app in both themes will ensure all elements are visible, light or dark.
What is OpacityMask?
According to the official documentation an opacity mask works by mapping its contents to the element or visual. The alpha channel of each of the brush's pixels are then used to determine the resulting opacity of the element or visual's corresponding pixels; the actual color of the brush is ignored. If a given portion of the brush is transparent, the corresponding portion of the element or visual becomes transparent. If a given portion of the brush is opaque, the opacity of the corresponding portion of the element or visual is unchanged. The opacity specified by the opacity mask is combined with any opacity settings present in the element or visual. For example, if an element is 25 percent opaque and an opacity mask is applied that transitions from fully opaque to fully transparent, the result is an element that transitions from 25 percent opacity to fully transparent.
For more information about OpacityMask take a look at the MSDN Documentation.
You can add Opacity Mask either by using Expression Blend or just by writing some more XAML in Visual Studio.
OpacityMask and Expression Blend
This is the easiest way to add Opacity Mask to any element in a Windows Phone 7 application. All you need to do is just to work with the visual designer and you do not need to add any custom code. However the biggest disadvantage is that the final result is a code full of unnecessary Margins, Paddings, Heights, etc generated automatically by Expression Blend.
Lets create a sample Windows Phone 7 application Expression Blend project. The first thing to do is to add some icons to the project we will add two dark and one light icon:
1. Adding OpacityMask to Rectangle:
In this example we will create a green rectangle and we will set its OpacityMask in the following way:
- Select the rectangle and go to the Properties tab
- Start Typing OpacityMask into the search field
- Select the OpacityMask field and the Tile Brush option
- Set the ImageSource to the desired image
- That`s it. Now you can see the result.
2.Adding OpacityMask to Button using Light and Dark icon:
In this example we will create a button and we will set its OpacityMask so that the button icon will be consistent in light/dark themes in the following way :
- Select the button and go to the Properties tab
- Start Typing OpacityMask into the search field
- Select the OpacityMask field and the Tile Brush option
- Set the ImageSource to the desired image
- Go to the Background section in the Properties tab and press the Brush Resources tab
- Select PhoneContrastBackgroundBrush brush
- That`s it. Now you can see the result.
NOTE: Do not forget to set the Background color properly otherwise your button will no be visible!
OpacityMask and VisualStudio
Here is how the source code should looks like. Note that we have optimized the code and removed all the unnecessary Margins.Padding etc. Note that Every UIElement exposes a property called OpacityMask which can be assigned a Brush instance.
1. Adding Opacity Mask to Rectangle:
<Rectangle Fill="#FF2CA90A" Height="116" Stroke="Black" Width="115">
<Rectangle.OpacityMask>
<ImageBrush Stretch="Fill" ImageSource="icons/appbar.delete.rest.png"/>
</Rectangle.OpacityMask>
</Rectangle>
2.Adding Opacity Mask to Button using Light and Dark icon:
<Button Height="87" Width="145" Background="{StaticResource PhoneContrastBackgroundBrush}">
<Button.OpacityMask>
<ImageBrush Stretch="Fill" ImageSource="icons/appbar.feature.video.rest.png"/>
</Button.OpacityMask>
</Button>
<Button Height="87" Width="145" Background="{StaticResource PhoneContrastBackgroundBrush}">
<Button.OpacityMask>
<ImageBrush Stretch="Fill" ImageSource="icons/appbar.feature.email.rest.png"/>
</Button.OpacityMask>
</Button>
3.Adding Opacity Mask to Ellipse using dark icon :
In this example we will create two ellipses in a grid and will add OpacityMask to the second one. The final goal is to create some kind of round image button.
<Grid Width="72" Height="72" Margin="0,-10">
<Ellipse Stroke="{StaticResource PhoneBorderBrush}" StrokeThickness="{StaticResource PhoneBorderThickness}"
Fill="{StaticResource PhoneSemitransparentBrush}"
Margin="{StaticResource PhoneTouchTargetOverhang}" />
<Ellipse Fill="{StaticResource PhoneForegroundBrush}" Margin="{StaticResource PhoneTouchTargetOverhang}">
<Ellipse.OpacityMask>
<ImageBrush ImageSource="icons/appbar.feature.email.rest.png"/>
</Ellipse.OpacityMask>
</Ellipse>
</Grid>
4. Define Opacity Mask programmatically in code behind:
Button btn = new Button();
btn.Height = 100;
btn.Width = 100;
btn.Background = Application.Current.Resources["PhoneContrastBackgroundBrush"] as SolidColorBrush;
ImageBrush image = new ImageBrush();
image.ImageSource = new BitmapImage( new Uri("icons/appbar.feature.email.rest.png", UriKind.Relative));
image.Stretch = Stretch.Fill;
btn.OpacityMask = image;
this.ContentPanel.Children.Add(btn);
5. Create a sample theme friendly Button with VisualStates:
In this example we will create a theme friendly image button with VisualStates by adding OpacityMask into the ControlTemplate:
<Style x:Key="SampleIconButton" 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="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="ContentArea">
<DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource PhoneBackgroundBrush}"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Background" Storyboard.TargetName="BackgroundBrush">
<DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource PhoneForegroundBrush}"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="BorderBrush" Storyboard.TargetName="BackgroundBrush">
<DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource PhoneForegroundBrush}"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Border x:Name="BackgroundBrush" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" CornerRadius="0" Margin="{StaticResource PhoneTouchTargetOverhang}">
<Grid x:Name="ContentArea" OpacityMask="{TemplateBinding Content}" Background="{TemplateBinding Foreground}"/>
</Border>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Button Style="{StaticResource SampleIconButton}" Height="100" Width="100">
<ImageBrush ImageSource="icons/appbar.delete.rest.png" Stretch="None"/>
</Button>
Here are some more screen shots:
You can find the full source code here:
I hope that the article was helpful.
You can also follow us on Twitter @winphonegeek
Comments
Awesome!
posted by: Kim on 2/22/2011 1:41:25 PM
Awesome! thank you so mush for this article.
COOOL- contacts list
posted by: Avinash on 2/23/2011 11:09:38 PM
Great Work! we really enjoying your posts!
Looking on some articles related to local storage.
One more problem I want to discuss here - How can we get the phone contact email/phone no list through APIs in the app, if possible pls let me know to proceeds my work :)
Thanks!!!!
RE:contacts list
posted by: windowsphonegeek on 2/24/2011 2:02:55 PM
Currently it is not possible to get a list of all contacts.
What you see on tasks here is the full extent of related functionality at the moment: Launchers and Choosers: introduction
Thanks
posted by: will on 3/9/2011 4:36:48 AM
Thanks for the post. I was previously using both a dark and light image, and checking what phone background was active, but this was a much more elegant solution. Thanks!
posted by: JonAlb on 7/21/2011 11:50:36 PM
An Excellent solution to an apparently simple problem.
Be careful when implementing this solution
posted by: joseharriaga on 10/22/2011 11:11:27 AM
OpacityMasks cannot be compute by the GPU. This means they are worked out by the UI thread. If you're using many OpacityMasks, this might kill your app's performance.
posted by: m.savazzi on 1/26/2012 1:04:56 AM
Thorws exception on WP7.1
Just compiled the sample :(
posted by: Richard Woo on 2/12/2012 7:56:47 PM
For WP7.1 Mango, in line 93 of MainPage.xaml, change PhoneBorderThickness to PhoneStrokeThickness
original:
<Ellipse Stroke="{StaticResource PhoneBorderBrush}" StrokeThickness="{StaticResource PhoneBorderThickness}"
new:
<Ellipse Stroke="{StaticResource PhoneBorderBrush}" StrokeThickness="{StaticResource PhoneStrokeThickness}"
Our Top Articles & Free books
- Our FREE e-book: "Windows Phone Toolkit In Depth" 2nd edition
- 400+ Windows Phone Development articles in our Article Index
- 21 WP7 Toolkit in Depth articles covering all controls
- 12 WP7 Coding4Fun Toolkit in Depth articles covering all controls
- Performance Tips when creating WP7 apps
- Creating a WP7 Custom Control in 7 Steps
- WP7 working with VisualStates: How to make a ToggleSwitch from CheckBox
- What makes a WP7 App successful
- Creating theme friendly UI in WP7 using OpacityMask
- Implementing Windows Phone 7 DataTemplateSelector and CustomDataTemplateSelector
- All about Splash Screens in WP7 – Creating animated Splash Screen
- Getting Started with Unit Testing in Silverlight for WP7
- WP7 WatermarkedTextBox custom control
Our Top Tips & Samples
- All about WP7 Isolated Storage series
- WP7 Dynamically Generating DataTemplate in code
- 5 tips for a successful WP7 Marketplace submission
- WP7: Navigating to a page in different assembly
- WP7 ContextMenu: answers to popular questions
- WP7 ListBox: answers to popular questions
- WP7 working with Images: Content vs Resource build action
- WP7 Element Binding samples
- WP7 working with XML: reading, filtering and databinding
- Drawing in WP7: #2 Drawing shapes with finger
- WP7 TextBox Light theme problems - the solution
- Changing the WP7 Panorama Background Image dynamically with Animation
