This
chapter is taken from book "Programming Windows Phone 7" by Charles
Petzold published by Microsoft press.
http://www.charlespetzold.com/phone/index.html
When you run the SilverlightHelloPhone program, and you
turn the phone or emulator sideways, you'll discover that the display
doesn't change to accommodate the new orientation, To fix that problem
you have to make change in the root PhoneApplicationPage tag, of MainPage.xaml
change the attribute.
SupportedOrientations="Portrait"
to: SupportedOrientations="PortraitOrLandscape"
SupportedOrientations is a property of PhoneApplicationPage.
It's set to a member of the SupportedPageOrientation
enumeration, either Portrait, Landscape, or PortraitOrLandscape. Now when you turn the phone or emulator sideways, the
contents of the page shift around accordingly:
Two of the most important properties in working with dynamic layout
are HorizontalAlignment and VerticalAlignment. on the other hand, if you
now needed to stack a bunch of text strings, you would probably find it
straightforward in XNA, but not so obvious in Silverlight. Rest assured
that there are ways to organize elements in Silverlight. A whole
category of elements called panels exist solely for that purpose. You
can even position elements based on pixel coordinates, if that's your
preference.
Normally a Grid organizes its content into cells identified by row
and column, but this program puts nine TextBlock elements in a
single-cell Grid to demonstrate the use of HorizontalAlignment and
VerticalAlignment in nine different combinations:
Example
<Grid
x:Name="ContentPanel"
Grid.Row="1"
Margin="12,0,12,0">
<TextBlock
Text="Top-Left"
VerticalAlignment="Top"
HorizontalAlignment="Left" />
<TextBlock
Text="Top-Center"
VerticalAlignment="Top"
HorizontalAlignment="Center" />
<TextBlock
Text="Top-Right"
VerticalAlignment="Top"
HorizontalAlignment="Right" />
<TextBlock
Text="Center-Left"
VerticalAlignment="Center"
HorizontalAlignment="Left" />
<TextBlock
Text="Center"
VerticalAlignment="Center"
HorizontalAlignment="Center" />
<TextBlock
Text="Center-Right"
VerticalAlignment="Center"
HorizontalAlignment="Right" />
<TextBlock
Text="Bottom-Left"
VerticalAlignment="Bottom"
HorizontalAlignment="Left" />
<TextBlock
Text="Bottom-Center"
VerticalAlignment="Bottom"
HorizontalAlignment="Center" />
<TextBlock
Text="Bottom-Right"
VerticalAlignment="Bottom"
HorizontalAlignment="Right" />
</Grid>
Output
Although this screen appears to show all the combinations, the
program does not actually show the default
settings of the HorizontalAlignment and VerticalAlignment
properties. The default settings are enumeration members named
Stretch. The HorizontalAlignment and VerticalAlignment
properties are very important in the layout system in Silverlight. So is Margin. Try adding a Margin setting to the first
TextBlock in this program:
<TextBlock
Text="Top-Left"
VerticalAlignment="Top"
HorizontalAlignment="Left"
Margin="100" />
Now there's a 100-pixel breathing room between the
TextBlock and the left and
top edges of the client area. The Margin property is of type
Thickness, a structure
that has four properties named
Left,
Top,
Right,
and
Bottom.
If you specify only one number in XAML, that's used for all four sides.
Padding:
is also of type Thickness,
and when used with the TextBlock, Padding is visually
indistinguishable from Margin. But they are definitely different:
Margin is space on the outside of the TextBlock;
Padding is space inside the TextBlock not occupied by the
text itself.
You're second guessing the size of the
TextBlock without knowing
as much about the element as the TextBlock itself. In some cases,
setting Width and Height
is appropriate, but not here. The
Width
and
Height
properties are of type
double,
and the default values are those special floating-point values called
Not a Number or NaN.
Some useful events are also available for obtaining information
involving element sizes. The
Loaded event is fired when
visuals are first arranged on the screen; SizeChanged is
supported by elements to indicate when they've changed size;
LayoutUpdated is useful when you want notification that a layout
cycle has occurred, such as occurs when orientation changes.
You can associate a particular event with an event handler right in
XAML, but the actual event handler must be implemented in code. When you
type an event name in XAML (such as SizeChanged)
Visual Studio will offer to create an event handler for you. That's what
I did with the SizeChanged event for the content grid:
<Grid
x:Name="ContentPanel"
Grid.Row="1"
Margin="12,0,12,0"
SizeChanged="ContentPanel_SizeChanged">
<TextBlock
Name="txtblk"
HorizontalAlignment="Center"
VerticalAlignment="Center"
/>
</Grid>
Assigning names to elements is one of two primary ways in which code
and XAML interact. The second way is for the element defined in XAML to
fire an event that is handled in code. Here's the handler for the SizeChanged
event of the content grid as Visual Studio created it:
Example
private
void ContentPanel_SizeChanged(object sender, SizeChangedEventArgs e)
{
txtblk.Text = String.Format("ContentPanel
size: {0}\n" +
"TitlePanel size: {1}\n"
+
"LayoutRoot size: {2}\n"
+
"MainPage size: {3}\n"
+
"Frame size: {4}",
e.NewSize,
new
Size(TitlePanel.ActualWidth,
TitlePanel.ActualHeight),
new
Size(LayoutRoot.ActualWidth,
LayoutRoot.ActualHeight),
new Size(this.ActualWidth,
this.ActualHeight),
Application.Current.RootVisual.RenderSize);
}
The first SizeChanged
event occurs when the page is created and laid out, that is, when the
content grid changes size from 0 to a finite value:
Orientation Events: To set SupportedOrientations to PortraitOrLandscape,
and try to write orientation-independent applications, Obviously there
is more to handling orientation changes than just setting the
SupportedOrientations property. If you need to perform any special handling, both
PhoneApplicationFrame and
PhoneApplicationPage include
OrientationChanged events.
PhoneApplicationPage supplements that event with a convenient
and equivalent protected overridable method called OnOrientationChanged.
The MainPage class in the SilverlightOrientationDisplay project
shows how to override OnOrientationChanged, but what it does with
this information is merely to display the current orientation. The
content grid in this project contains a simple TextBlock:
<Grid
x:Name="ContentPanel"
Grid.Row="1"
Margin="12,0,12,0">
<TextBlock
Name="txtblk"
HorizontalAlignment="Center"
VerticalAlignment="Center"
/>
</Grid>
Here's the complete code-behind file. The constructor initializes the
TextBlock text with the current value of the Orientation property, which is a member of the PageOrientation
enumeration:
using
System.Windows.Controls;
using
Microsoft.Phone.Controls;
namespace
SilverlightOrientationDisplay
{
public
partial class
MainPage :
PhoneApplicationPage
{
public
MainPage()
{
InitializeComponent();
txtblk.Text =
Orientation.ToString();
}
protected
override void
OnOrientationChanged(OrientationChangedEventArgs
args)
{
txtblk.Text =
args.Orientation.ToString();
base.OnOrientationChanged(args);
}
}
}
XNA Orientation
By default, XNA for Windows Phone is set up for a landscape
orientation, perhaps to be compatible with other screens on which games
are played. If you prefer designing your game for a portrait display,
it's easy to do that. In the constructor of the Game1 class of XnaHelloPhone, try inserting the following
statements:
graphics.PreferredBackBufferWidth = 320;
graphics.PreferredBackBufferHeight = 480;
It's also possible to have your XNA games respond to orientation
changes, but they'll definitely have to be restructured a bit. The
simplest type of restructuring to accommodate orientation changes is
demonstrated in the XnaOrientableHelloPhone project. The fields now
include a textSize
variable:
Example
public
class Game1
: Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
string text
= "Hello, Windows Phone 7!";
SpriteFont segoe14;
Vector2 textSize;
Vector2 textPosition;
…
}
The Game1
constructor includes a statement that sets the
SupportedOrientations property of the graphics
field:
public
Game1()
{
graphics = new
GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
// Allow portrait mode as well
graphics.SupportedOrientations = DisplayOrientation.Portrait |
DisplayOrientation.LandscapeLeft |
DisplayOrientation.LandscapeRight;
// Frame rate is 30 fps by default for Windows Phone.
TargetElapsedTime = TimeSpan.FromTicks(333333);
}
Simple Clocks (Very
Simple Clocks)
you can attach handlers to events entirely in code as well.
One handy class for Silverlight programs is DispatcherTimer,
which periodically nudges the program with a Tick
event and lets the program do some work. A timer
is essential for a clock program, for example.
The content grid of the SilverlightSimpleClock project contains just
a centered TextBlock:
<Grid
x:Name="ContentPanel"
Grid.Row="1"
Margin="12,0,12,0">
<TextBlock
Name="txtblk"
HorizontalAlignment="Center"
VerticalAlignment="Center"
/>
</Grid>
Here's the entire code-behind file. Notice the using directive
for the System.Windows.Threading namespace, which isn't included by default.
That's the namespace where DispatcherTimer
resides:
using
System;
using
System.Windows.Threading;
using
Microsoft.Phone.Controls;
namespace
SilverlightSimpleClock
{
public
partial class
MainPage :
PhoneApplicationPage
{
public MainPage()
{
InitializeComponent();
DispatcherTimer tmr = new DispatcherTimer();
tmr.Interval
= TimeSpan.FromSeconds(1);
tmr.Tick +=
OnTimerTick;
tmr.Start();
}
void OnTimerTick(object
sender, EventArgs args)
{
txtblk.Text =
DateTime.Now.ToString();
}
}
}
The constructor initializes the
DispatcherTimer,
instructing it to call OnTimerTick
once every second. The event handler simply converts the
current time to a string to set it to the
TextBlock.
An XNA clock program doesn't need a timer because a timer is
effectively built into the normal game loop. However, the clock I want to code
here won't display milliseconds so the display only needs to be updated every
second. For that reason it uses the SuppressDraw method to inhibit superfluous
Draw calls.
Here are the XnaSimpleClock fields:
Example
public
class Game1
: Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
SpriteFont segoe14;
Viewport viewport;
Vector2 textPosition;
StringBuilder text = new
StringBuilder();
DateTime lastDateTime;
…
}
The
LoadContent method gets the font and the viewport of
the display:
protected
override void
LoadContent()
{
spriteBatch = new
SpriteBatch(GraphicsDevice);
segoe14 = this.Content.Load<SpriteFont>("Segoe14");
viewport = this.GraphicsDevice.Viewport;
}
The logic to compare two
DateTime values to see
if the time has changed is just a little tricky because
DateTime objects
obtained during two consecutive Update
calls will
always be different because they have will have
different Millisecond
fields. For this reason, a new
DateTime is calculated
based on the current time obtained from
DateTime.Now, but subtracting the milliseconds:
protected
override void
Update(GameTime gameTime)
{
// Allows the game to exit
if (GamePad.GetState(PlayerIndex.One).Buttons.Back
== ButtonState.Pressed) this.Exit();
// Get DateTime with no milliseconds
DateTime dateTime = DateTime.Now;
dateTime = dateTime - new
TimeSpan(0, 0, 0, 0, dateTime.Millisecond);
if (dateTime != lastDateTime)
{
text.Remove(0, text.Length);
text.Append(dateTime);
Vector2 textSize = segoe14.MeasureString(text); textPosition =
new Vector2((viewport.Width - textSize.X) / 2,
(viewport.Height - textSize.Y) / 2);
lastDateTime = dateTime;
}
else
{
SuppressDraw();
}
base.Update(gameTime);
}
If the time has not changed,
SuppressDraw is
called. The result: Draw
is called only once per second.
DrawString also has an overload for StringBuilder:
protected
override void
Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.Navy);
spriteBatch.Begin();
spriteBatch.DrawString(segoe14, text, textPosition, Color.White);
spriteBatch.End();
base.Draw(gameTime);
}
Output Result
Conclusion
I hope this article help you to
understand how to
managed the orientation in silverlight and xna windows phone 7 application.