Working with Attributes and Resources of XAML in Windows Phone 7


This chapter is taken from book "Programming Windows Phone 7" by Charles Petzold published by Microsoft press. http://www.charlespetzold.com/phone/index.html

This word "attribute" has different meanings in .NET and XML; here I'm talking about the .NET attribute, which refers to some additional information that is associated with a class or a member of that class.

If you want to get more explicit in your markup, you can include a property element for the Children property:

<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
    <
Grid.Children>
        <
TextBlock Text="Hello, Windows Phone 7!" />
    </
Grid.Children>
</
Grid>

Similarly, PhoneApplicationPage derives from UserControl, which also has a ContentProperty attribute:

[ContentPropertyAttribute("Content", true)]
public class UserControl : Control

The ContentProperty attribute of UserControl is the Content property. (That sentence makes more sense when you see it on the page rather than when you read it out load!)

Suppose you want to put two TextBlock elements in a Grid, and you want the Grid to have a LinearGradientBrush for its Background. You can put the Background property element first within the Grid tags followed by the two TextBlock elements:

<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
    <
Grid.Background>
        <
LinearGradientBrush>
            <
GradientStop Offset="0" Color="LightCyan" />
            <
GradientStop Offset="1" Color="LightPink" />
        </
LinearGradientBrush>
    </
Grid.Background>
    <
TextBlock Text="TextBlock #1"
               HorizontalAlignment="Left" />
    <
TextBlock Text="TextBlock #2" HorizontalAlignment="Right" />
</
Grid>

It's also legal to put the two TextBlock elements first and the Background property element last:

<
Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
    <
TextBlock Text="TextBlock #1" HorizontalAlignment="Left" />
    <
TextBlock Text="TextBlock #2"
                    HorizontalAlignment="Right" />
    <
Grid.Background>
        <
LinearGradientBrush>
            <
GradientStop Offset="0" Color="LightCyan" />
            <
GradientStop Offset="1" Color="LightPink" />
        </
LinearGradientBrush>
    </
Grid.Background>
</
Grid>

But putting the Background property element between the two TextBlock elements simply won't work:

<
Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
    <
TextBlock Text="TextBlock #1" HorizontalAlignment="Left" />
    <!-- Not a legal place for the property element! -->
    <Grid.Background>
        <
LinearGradientBrush>
            <
GradientStop Offset="0" Color="LightCyan" />
            <
GradientStop Offset="1" Color="LightPink" />
        </
LinearGradientBrush>
    </
Grid.Background>
    <
TextBlock Text="TextBlock #2"
                    HorizontalAlignment="Right" />
</
Grid>

The precise problem with this syntax is revealed when you put in the missing property elements for the Children property of the Grid:

<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
    <
Grid.Children>
        <
TextBlock Text="TextBlock #1"
                        HorizontalAlignment="Left" />
    </
Grid.Children>
    <!-- Not a legal place for the property element! -->
    <Grid.Background>
        <
LinearGradientBrush>
            <
GradientStop Offset="0" Color="LightCyan" />
            <
GradientStop Offset="1" Color="LightPink" />
        </
LinearGradientBrush>
    </
Grid.Background>
    <
Grid.Children>
        <
TextBlock Text="TextBlock #2"
                        HorizontalAlignment="Right" />
    </
Grid.Children>
</
Grid>

Now it's obvious that the Children property is being set twice-and that's clearly illegal.

Resources Collection

Computer programming is all about the avoidance of repetition. XAML would seem to be a particularly treacherous area for repetition because it's just markup and not a real programming language. The most generalized solution to repetitive markup is the Silverlight style. But a prerequisite to styles is a more generalized sharing mechanism. This is called the resource, and right away we need to distinguish between the resources.

XAML resources are always instances of a particular .NET class or structure, either an existing class or structure or a custom class and the sharing of resources immediately disqualifies many classes from being defined as XAML resources.

Here's a Resources collection for a page class that derives from PhoneApplicationPage:

<phone:PhoneApplicationPage ...>
    <
phone:PhoneApplicationPage.Resources>
       ...
    </phone:PhoneApplicationPage.Resources>
    ...
</phone:PhoneApplicationPage>

The resources defined in the Resources collection on a FrameworkElement are available only within that element and nested elements; the resources defined in the Application class are available throughout the application.

Sharing Brushes

Let's suppose your page contains several TextBlock elements, and you want to apply the same LinearGradientBrush to the Foreground of each of them. This is an ideal use of a resource the resource must be defined before it is used, and it can only be accessed by the same element or a nested element.

The ResourceSharing project defines all resources and references them in two TextBlock elements. Here's the complete resource section:

<phone:PhoneApplicationPage.Resources>
    <
LinearGradientBrush x:Key="brush">
        <
GradientStop Offset="0" Color="Pink" />
        <
GradientStop Offset="1" Color="SkyBlue" />
    </
LinearGradientBrush>
    <
Thickness x:Key="margin">
        12 96
    </Thickness>
    <
system:Double x:Key="fontsize">
        48
    </system:Double>
</
phone:PhoneApplicationPage.Resources>

The content grid contains the two TextBlock elements:

<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
    <
TextBlock Text="Whadayasay?"
               Foreground="{StaticResource brush}"
               Margin="{StaticResource margin}"
               FontSize="{StaticResource fontsize}"
               HorizontalAlignment="Left"
               VerticalAlignment="Top" />
    <
TextBlock Text="Fuhgedaboudit!"
               Foreground="{StaticResource brush}"
               Margin="{StaticResource margin}"
               FontSize="{StaticResource fontsize}"
               HorizontalAlignment="Right"
               VerticalAlignment="Bottom" />
</
Grid>

The screen shot demonstrates that it works:

sev1.gif

x:Key and x:Name

If you need to reference a XAML resource from code, you can simply index the Resources property with the resource name.

this.Resources["brush"]

If you have resources defined in other Resource collections in the same XAML file, you can retrieve those as well.

ContentPanel.Resources["margin"]

Introduction to Styles

One very common item in a Resources collection is a Style, which is basically a collection of property assignments for a particular element type. Besides a key, the Style also requires a TargetType:

<Style x:Key="txtblkStyle"
         TargetType
="TextBlock">
         ...
</Style>

you can use property-element syntax with the Value property to embed the brush right in the Style definition. That's how it's done in the Resources collection of the StyleSharing project:

<phone:PhoneApplicationPage.Resources>
    <
Style x:Key="txtblkStyle"
           TargetType
="TextBlock">
        <
Setter Property="HorizontalAlignment" Value="Center" />
        <
Setter Property="VerticalAlignment" Value="Center" />
        <
Setter Property="Margin" Value="12 96" />
        <
Setter Property="FontSize" Value="48" />
        <
Setter Property="Foreground">
            <
Setter.Value>
                <
LinearGradientBrush>
                    <
GradientStop Offset="0" Color="Pink" />
                    <
GradientStop Offset="1" Color="SkyBlue" />
                </
LinearGradientBrush>
            </
Setter.Value>
        </
Setter>
    </
Style>
</
phone:PhoneApplicationPage.Resources>

To apply this style to an element of type TextBlock, set the Style property (which is defined by FrameworkElement so every kind of element has it):

<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
    <
TextBlock Text="Whadayasay?"
               Style="{StaticResource txtblkStyle}"
               HorizontalAlignment="Left"
               VerticalAlignment="Top" />
    <
TextBlock Text="Fuhgedaboudit!"
               Style="{StaticResource txtblkStyle}"
               HorizontalAlignment="Right"
               VerticalAlignment="Bottom" />
</
Grid>

The display looks the same as the previous program, which teaches an important lesson. Notice that values of HorizontalAlignment and VerticalAlignment are defined in the Style, yet these are overridden by local settings in the two TextBlock elements. But the Foreground set in the Style overrides the value normally inherited through the visual tree.

Style Inheritance

Styles can enhance or modify other styles through the process of inheritance. Set the Style property BasedOn to a previously defined Style. Here's the Resources collection of the StyleInheritance project:

<
phone:PhoneApplicationPage.Resources>
    <
Style x:Key="txtblkStyle"
           TargetType="TextBlock">
        <
Setter Property="HorizontalAlignment" Value="Center" />
        <
Setter Property="VerticalAlignment" Value="Center" />
        <
Setter Property="Margin" Value="12 96" />
        <
Setter Property="FontSize" Value="48" />
        <
Setter Property="Foreground">
            <
Setter.Value>
                <
LinearGradientBrush>
                    <
GradientStop Offset="0" Color="Pink" />
                    <
GradientStop Offset="1" Color="SkyBlue" />
                </
LinearGradientBrush>
            </
Setter.Value>
        </
Setter>
    </
Style>
    <Style x:Key="upperLeftStyle"
           TargetType="TextBlock"
           BasedOn="{StaticResource txtblkStyle}">
        <
Setter Property="HorizontalAlignment" Value="Left" />
        <
Setter Property="VerticalAlignment" Value="Top" />
    </
Style>
    <
Style x:Key="lowerRightStyle"
           TargetType="TextBlock"
           BasedOn="{StaticResource txtblkStyle}">
        <
Setter Property="HorizontalAlignment" Value="Right" />
        <
Setter Property="VerticalAlignment" Value="Bottom" />
    </
Style>
</
phone:PhoneApplicationPage.Resources>

The two new Style definitions at the end override the HorizontalAlignment and VerticalAlignment properties set in the earlier style. This allows the two TextBlock elements to reference these two different styles:

<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
    <
TextBlock Text="Whadayasay?"
               Style="{StaticResource upperLeftStyle}" />
    <
TextBlock Text="Fuhgedaboudit!"
               Style="{StaticResource lowerRightStyle}" />
</
Grid>

Themes

Windows Phone 7 predefines many resources that you can use throughout your application with the StaticResource markup extension. There are predefined colors, brushes, font names, font sizes, margins, and text styles. Some of them show up in the root element of MainPage.xaml to supply the defaults for the whole page:

FontFamily="{StaticResource PhoneFontFamilyNormal}"
FontSize="{StaticResource PhoneFontSizeNormal}"
Foreground="{StaticResource PhoneForegroundBrush}"

Gradient Accents

You might want to use the user's preferred accent color in your program, but as a gradient brush. In other words, you want the same hue, but you want to get darker or lighter versions. In code, this is fairly easy by manipulating the red, green, and blue components of the color.

It's also fairly easy in XAML, as the GradientAccent project demonstrates:

<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
    <
Grid.Background>
        <
LinearGradientBrush StartPoint="0 0" EndPoint="1 0">
            <
GradientStop Offset="0" Color="White" />
            <
GradientStop Offset="0.5" Color="{StaticResource PhoneAccentColor}" />
            <
GradientStop Offset="1" Color="Black" />
        </
LinearGradientBrush>
    </
Grid.Background>
</
Grid>

sev2.gif

You can get a more subtle affect by changing the gradient offsets. These can actually be set outside the range of 0 to 1, perhaps like this:

<
LinearGradientBrush StartPoint="0 0" EndPoint="1 0">
    <
GradientStop Offset="-1" Color="White" />
    <
GradientStop Offset="0.5" Color="{StaticResource PhoneAccentColor}" />
    <
GradientStop Offset="2" Color="Black" />
</
LinearGradientBrush>

Now the gradient goes from White at an offset of -1 to the accent color at 0.5 to Black at 2. But you're only seeing the section of the gradient between 0 and 1, so the White and Black extremes are not here:

sev3.gif


Similar Articles