This chapter
is taken from book "Programming Windows Phone 7" by Charles Petzold published by
Microsoft press.
http://www.charlespetzold.com/phone/index.html
Handling Manipulation Events: Transforms are also a good way to handle
manipulation events. Here's a ball sitting in the middle of the content grid:
<Grid
x:Name="ContentPanel"
Grid.Row="1"
Margin="12,0,12,0">
<Ellipse
Width="200"
Height="200"
RenderTransformOrigin="0.5
0.5"
ManipulationDelta="OnEllipseManipulationDelta">
<Ellipse.Fill>
<RadialGradientBrush
Center="0.4
0.4"
GradientOrigin="0.4
0.4">
<GradientStop
Offset="0"
Color="White"
/>
<GradientStop
Offset="1"
Color="{StaticResource
PhoneAccentColor}"
/>
</RadialGradientBrush>
</Ellipse.Fill>
<Ellipse.RenderTransform>
<CompositeTransform
/>
</Ellipse.RenderTransform>
</Ellipse>
</Grid>
Notice the CompositeTransform. It has no name so the code will have to reference it through the Ellipse element. (This is a good strategy to use if you're handling more than one element in a single event handler.)
The code-behind file just handles the ManipulationDelta event from the Ellipse:
namespace DragAndScale
{
public partialclass MainPage : PhoneApplicationPage
{
public MainPage()
{
InitializeComponent();
}
void OnEllipseManipulationDelta(object sender,ManipulationDeltaEventArgs args)
{
Ellipse ellipse = senderas Ellipse;
CompositeTransform xform = ellipse.RenderTransform asCompositeTransform;
if (args.DeltaManipulation.Scale.X > 0 || args.DeltaManipulation.Scale.Y > 0)
{
double maxScale =Math.Max(args.DeltaManipulation.Scale.X,
args.DeltaManipulation.Scale.Y);
xform.ScaleX *= maxScale;
xform.ScaleY *= maxScale;
}
xform.TranslateX += args.DeltaManipulation.Translation.X;
xform.TranslateY += args.DeltaManipulation.Translation.Y;
args.Handled = true;
}
}
}
For handling anything other than taps, the ManipulationDelta event is crucial. This is the event that consolidates one or more fingers on an element into translation and scaling information.
The Border Element
The TextBlockdoesn't include any kind of border that you can draw around the text. Fortunately Silverlight has a Border element that you can use to enclose a TextBlock or any other type of element. The Border has a property named Child of type UIElement, which means you can only put one element in a Border; however, the element you put in the Border can be a panel, and you can then add multiple elements to that panel.
You can center the TextBlock within
the Border:
<Grid
x:Name="ContentPanel"
Grid.Row="1"
Margin="12,0,12,0">
<Border
Background="Navy"
BorderBrush="Blue"
BorderThickness="16"
CornerRadius="25">
<TextBlock
Text="Hello,
Windows Phone 7!"
HorizontalAlignment="Center"
VerticalAlignment="Center"
/>
</Border>
</Grid>
Or, you can center the Border within the Grid:
<Grid
x:Name="ContentPanel"
Grid.Row="1"
Margin="12,0,12,0">
<Border
Background="Navy"
BorderBrush="Blue"
BorderThickness="16"
CornerRadius="25"
HorizontalAlignment="Center"
VerticalAlignment="Center">
<TextBlock
Text="Hello,
Windows Phone 7!" />
</Border>
</Grid>
At this point, the Border contracts in size to become only large enough to
fit the TextBlock. You can also set the HorizontalAlignment and
VerticalAlignment properties of the TextBlock but they would now have no effect.
You can give the TextBlock a little breathing room inside the border by either
setting the Margin or Padding property of the TextBlock, or the Padding property
of the Border:
And now we have an attractive
Border surrounding the TextBlock. The
BorderThickness property is of type Thickness, the same structure used for
Margin or Padding, so you can potentially have four different thicknesses for
the four sides. The CornerRadius property is of type CornerRadius, a structure
that also lets you specify four different values for the four corners. The
Background and BorderBrush properties are of type Brush,
so you can use gradient brushes.
If you want a
Border with a "normal" thickness, you can use one
of the pre-defined resources:
<Border BorderThickness="{StaticResource
PhoneBorderThickness}"
This is 3 pixels in width. The PhoneStrokeThickness
resource also provides that same value.
What happens if you set a
RenderTransform on the TextBlock? Try this:
<Grid
x:Name="ContentPanel"
Grid.Row="1"
Margin="12,0,12,0">
<Border
Background="Navy"
BorderBrush="Blue"
BorderThickness="16"
CornerRadius="25"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Padding="20">
<TextBlock
Text="Hello,
Windows Phone 7!"
RenderTransformOrigin="0.5
0.5">
<TextBlock.RenderTransform>
<RotateTransform
Angle="45"
/>
</TextBlock.RenderTransform>
</TextBlock>
</Border>
</Grid>
Here's what you get:
The RenderTransform property is called a render transform for a reason: It
only affects rendering. It does not affect how the element is perceived in the
layout system. Your spirits might perk up, however, when you try moving the
RenderTransform (and RenderTransformOrigin) from the TextBlock to the Border,
like this:
<Grid
x:Name="ContentPanel"
Grid.Row="1"
Margin="12,0,12,0">
<Border
Background="Navy"
BorderBrush="Blue"
BorderThickness="16"
CornerRadius="25"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Padding="20"
RenderTransformOrigin="0.5
0.5">
<Border.RenderTransform>
<RotateTransform
Angle="45"
/>
</Border.RenderTransform>
<TextBlock
Text="Hello,
Windows Phone 7!" />
</Border>
</Grid>
Transforms affect not only the element to which they are applied, but all
child elements as this screen shot makes clear:
TextBlock Properties and Inlines
The TextBlock element has five font-related properties: FontFamily, FontSize,
FontStretch, FontStyle, and FontWeight, TextBlock also has a TextDecorations
property. Although this property seems to be very generalized.
However, you might be surprised to learn that the ContentProperty attribute
of TextBlock is not the Text property but an entirely different property named
Inlines. This Inlines property is of type InlineCollection-a collection of
objects of type Inline, namely LineBreak and Run. These make TextBlock much more
versatile. Run is interesting because it too has FontFamily, FontSize,
FontStretch, FontStyle, FontWeight, Foreground, and TextDecorations properties,
so you can make your text very fancy:
<TextBlock FontSize="36"
TextWrapping="Wrap">
This is
some <Run FontWeight="Bold">bold</Run> text and
some <Run FontStyle="Italic">italic</Run> text and
some <Run Foreground="Red">red</Run> text and
some <Run TextDecorations="Underline">underlined</Run> text
and some <Run FontWeight="Bold"
FontStyle="Italic"
Foreground="Cyan"
FontSize="72"
TextDecorations="Underline">big</Run> text.
</TextBlock>
In the Visual Studio design view, you might see the text within the Run tags not properly separated from the text outside the Run tags. This is an error. When you actually run the program in the emulator, it looks fine:
The FontFamilies program lists all the FontFamily values that Visual Studio's
Intellisense tells us are valid:
<Grid
x:Name="ContentPanel"
Grid.Row="1"
Margin="12,0,12,0">
<TextBlock
FontSize="24">
<Run
FontFamily="Arial">Arial</Run><LineBreak
/>
<Run
FontFamily="Arial
Black">Arial
Black</Run><LineBreak
/>
<Run
FontFamily="Calibri">Calibri</Run><LineBreak
/>
<Run
FontFamily="Comic
Sans MS">Comic
Sans MS</Run><LineBreak
/>
<Run
FontFamily="Courier
New">Courier
New</Run><LineBreak
/>
<Run
FontFamily="Georgia">Georgia</Run><LineBreak
/>
<Run
FontFamily="Lucida
Sans Unicode">Lucida
Sans Unicode</Run><LineBreak
/>
<Run
FontFamily="Portable
User Interface">Portable
User Interface</Run><LineBreak
/>
<Run
FontFamily="Segoe
WP">Segoe
WP</Run><LineBreak
/>
<Run
FontFamily="Segoe
WP Black">Segoe
WP Black</Run><LineBreak
/>
<Run
FontFamily="Segoe
WP Bold">Segoe
WP Bold</Run><LineBreak
/>
<Run
FontFamily="Segoe
WP Light">Segoe
WP Light</Run><LineBreak
/>
<Run
FontFamily="Segoe
WP Semibold">Segoe
WP Semibold</Run><LineBreak
/>
<Run
FontFamily="Segoe
WP SemiLight">Segoe
WP SemiLight</Run><LineBreak
/>
<Run
FontFamily="Tahoma">Tahoma</Run><LineBreak
/>
<Run
FontFamily="Times
New Roman">Times
New Roman</Run><LineBreak
/>
<Run
FontFamily="Trebuchet
MS">Trebuchet
MS</Run><LineBreak
/>
<Run
FontFamily="Verdana">Verdana</Run><LineBreak
/>
<Run
FontFamily="Webdings">Webdings</Run>
(Webdings)
</TextBlock>
</Grid>
Here's the result:
More on Images
The ImageExperiment project contains a folder named Images containing a file named BuzzAldrinOnTheMoon.png, which is the famous photograph taken with a Hasselblad camera by Neil Armstrong on July 21 st 1969. The photo is 288 pixels square. The file is referenced in the MainPage.xaml file like this:
<Grid
x:Name="ContentPanel"
Grid.Row="1"
Margin="12,0,12,0"
Background="{StaticResource
PhoneAccentBrush}">
<Image
Source="Images/BuzzAldrinOnTheMoon.png"
/>
</Grid>
I've also give the content grid a Background brush of the
accent color just to make the photo stand out a little better. Here's how it
appears in landscape mode: By
You can set transforms on the Image element with the same ease that you set
them on TextBlock elements:
<Image
Source="Images/BuzzAldrinOnTheMoon.png"
RenderTransformOrigin="0.5 0.5">
<Image.RenderTransform>
<RotateTransform
Angle="30" />
</Image.RenderTransform>
</Image>
Here it is:
Modes of Opacity
UIElement defines an Opacity property that you can set to a value between 0
and 1 to make an element (and its children) more or less transparent. But a
somewhat more interesting property is OpacityMask, which can "fade out" part of
an element.
For example, you can apply a RadialGradientBrush to the OpacityMask property
of an Image element:
<Image
Source="Images/BuzzAldrinOnTheMoon.png">
<Image.OpacityMask>
<RadialGradientBrush>
<GradientStop
Offset="0"
Color="White" />
<GradientStop
Offset="0.8"
Color="White" />
<GradientStop
Offset="1"
Color="Transparent" />
</RadialGradientBrush>
</Image.OpacityMask>
</Image>
Notice that the RadialGradientBrush is opaque in the center, and continues to
be opaque until a radius of 0.8, at which point the gradient goes to fully
transparent at the edge of the circle. Here's the result, a very nice effect
that looks much fancier than the few lines of XAML would seem to imply: