This chapter
is taken from book "Programming Windows Phone 7" by Charles Petzold published by
Microsoft press.
http://www.charlespetzold.com/phone/index.html
The Basic Button
The standard Silverlight Button is much more flexible than the ApplicationBar
buttons, as well as being easier to use. You can put a Button in the content
Grid as simply as this:
<Grid
x:Name="ContentPanel"
Grid.Row="1"
Margin="12,0,12,0">
<Button
Content="Click me!" />
</Grid>
By default, the Button fills the Grid:
It has a simple white border and displays the text
string assigned to its Content
property. If you put the Button in a horizontal StackPanel, it will be only as
wide as it needs to be to fit the content; the opposite effect happens when you
switch the StackPanel orientation to Vertical. Or you can set the
HorizontalAlignment and VerticalAlignment properties to anything other than
Stretch and the border in the Button is an actual Border and the content of the
Button (in this example) is an actual TextBlock. Earlier I mentioned that the
Control class defines a bunch of properties normally associated with the Border
and the TextBlock. You can set some of those properties like so:
<Grid
x:Name="ContentPanel"
Grid.Row="1"
Margin="12,0,12,0">
<Button
Content="Click me!"
FontSize="48"
FontStyle="Italic"
Foreground="Red"
Background="Blue"
BorderThickness="10"
BorderBrush="Yellow"
Padding="20"
HorizontalAlignment="Center"
VerticalAlignment="Center"
/>
</Grid>
As you might hope (or perhaps fear), these property settings are reflected in
the button's appearance:
The Concept of Content
Button derives from Control but it also derives from ContentControl, which is
the class that provides the button's Content property. You can pull out the
Content property as a property element and if you want to put text between the
button content you'll need an XML namespace declaration for the
System namespace:
xmlns:system="clr-namespace:System;assembly=mscorlib"
The Content property is of type object, and you really can set the Content
property to pretty much anything along with the string like
Double value, DateTime, and put a Color in the Button :
<Button>
<system:String>Click
this button</system:String>
<system:Double>1E5</system:Double>
<system:DateTime>October
1, 2010, 9:30 PM</system:DateTime>
<Color>Cyan</Color>
<SolidColorBrush
Color="Cyan" />
</Button>
And you can put an image with text also inside the same button that's how you
get that:
<Button
HorizontalAlignment="Center"
VerticalAlignment="Center">
<StackPanel>
<Image
Source="Images/BuzzAldrinOnTheMoon.png"
Stretch="None" />
<TextBlock
Text="Click this button!"
TextAlignment="Center" />
</StackPanel>
</Button>
ContentControl is not
the only class that defines a property named Content.
UserControl-the
class from which PhoneApplicationPage derives by way of Page-also
defines a Content property. It's natural to assume that
ContentControl and
UserControl are related in
some way, but it's actually also a sibling relationship.
Theme Styles and Precedence
Put the TextBlock in a
Button. You can make the text very large by setting
the FontSize on the
TextBlock and you can also achieve the same effect by
setting the FontSize on the Button itself.
<Grid
x:Name="ContentPanel"
Grid.Row="1"
Margin="12,0,12,0">
<Button
HorizontalAlignment="Center"
VerticalAlignment="Center">
<TextBlock
Text="Hello!"
FontSize="96" />
</Button>
</Grid>
But what doesn't work is setting the FontSize on the
PhoneApplicationPage. It seems as if property
inheritance should cause the value to trickle down to the TextBlock:
<phone:PhoneApplicationPage
...
FontSize="96"
... >
...
<Grid
x:Name="ContentPanel"
Grid.Row="1"
Margin="12,0,12,0">
<Button
HorizontalAlignment="Center"
VerticalAlignment="Center">
<TextBlock
Text="Hello!" />
</Button>
</Grid>
...
</phone:PhoneApplicationPage>
But it doesn't work. Something is blocking the
TextBlock from inheriting that FontSize value and button is defined in the System.Windows library, and that library also
contains a default style and template for the Button.
This is known as a theme
style, and for the Button
it includes a style setting for the FontSize property.
The Button Hierarchy
This class hierarchy is complete beginning with the ButtonBase class:
Control (abstract)
ContentControl
ButtonBase(abstract)
Button
HyperlinkButton
RepeatButton(sealed)
ToggleButton
CheckBox
RadioButton
It's actually ButtonBase that defines the Click event and the ClickMode property.
Toggling a Stopwatch
One handy application on a phone is a stopwatch-an ideal use for a ToggleButton as well as the Stopwatch class defined in the System.Diagnostics
namespace.
The content area in the XAML file is a bit more extensive than you might
expect because it includes a type of "dialog box" that's used by the user to
select the elapsed time format. So as not to overwhelm you, only the portion of
the content area devoted to the operation of the stopwatch is shown here. It
consists of just a ToggleButton
to turn the stopwatch on and off, and a
TextBlock to display
the elapsed time.
<Grid
x:Name="ContentPanel"
Grid.Row="1"
Margin="12,0,12,0">
<!--
Stopwatch display -->
<Grid
VerticalAlignment="Center"
Margin="25 0">
<Grid.RowDefinitions>
<RowDefinition
Height="Auto" />
<RowDefinition
Height="Auto" />
</Grid.RowDefinitions>
<TextBlock
Name="elapsedText"
Text="0"
Grid.Row="0"
FontFamily="Arial"
FontSize="{StaticResource
PhoneFontSizeExtraLarge}"
TextAlignment="Center"
Margin="0 0 0 50"/>
<ToggleButton
Name="startStopToggle"
Content="Start"
Grid.Row="1"
Checked="OnToggleButtonChecked"
Unchecked="OnToggleButtonChecked"
/>
</Grid>
<!--Rectangle to simulate disabling -->
...
<!-- "Dialog Box" to select TimeSpan
formatting -->
...
</Grid>
The code-behind file defines just three fields;
using directives are required for
System.Diagnostics and System.Globaliztion.
public
partial
class
MainPage
: PhoneApplicationPage
{
Stopwatch stopwatch =
new
Stopwatch();
TimeSpan
suspensionAdjustment = new
TimeSpan();
string
decimalSeparator =
NumberFormatInfo.CurrentInfo.NumberDecimalSeparator;
public
MainPage()
{
InitializeComponent();
DisplayTime();
}
....
void
DisplayTime()
{
TimeSpan elapsedTime = stopwatch.Elapsed +
suspensionAdjustment;
string str =
null;
switch ((Application.Current as
App).ElapsedTimeFormat)
{
case
ElapsedTimeFormat.HourMinuteSecond:
str = String.Format("{0:D2}
{1:D2} {2:D2}{3}{4:D2}",
elapsedTime.Hours, elapsedTime.Minutes,
elapsedTime.Seconds, decimalSeparator,
elapsedTime.Milliseconds / 10);
break;
case
ElapsedTimeFormat.Seconds:
str = String.Format("{0:F2}
sec", elapsedTime.TotalSeconds);
break;
case
ElapsedTimeFormat.Milliseconds:
str = String.Format("{0:F0}
msec", elapsedTime.TotalMilliseconds);
break;
}
elapsedText.Text = str;
}
....
}
For complete code please download the source
code given at top of the article:-
Here it is in action:
TextBox and Keyboard Input
The two types of text-entry controls available in Silverlight for Windows
Phone are TextBox,
which allows typing and editing single-line or multiline plain unformatted text,
and PasswordBox,
which briefly displays each letter you type but then replaces it with another
character, by default an asterisk.
These are the only two ways your program can get input from the hardware keyboard of the phone (if it exists) or invoke the Software Input Panel (SIP), the virtual on-screen keyboard.
Let's just jump right into a program. The OneTimeText program is designed to let you send an SMS (Short Message Service) text message to a particular phone number. The program requires you to type in that phone number but doesn't save it anywhere. That's why I called the program "one time" text.
Here's the content area:
<Grid
x:Name="ContentPanel"
Grid.Row="1"
Margin="12,0,12,0">
<Grid
Margin="24">
<Grid.RowDefinitions>
<RowDefinition
Height="Auto" />
<RowDefinition
Height="Auto" />
<RowDefinition
Height="Auto" />
<RowDefinition
Height="*" />
<RowDefinition
Height="Auto" />
</Grid.RowDefinitions>
<TextBlock
Grid.Row="0"
Text="phone number"
Style="{StaticResource
PhoneTextSmallStyle}" />
<TextBox
Name="toTextBox"
Grid.Row="1"
InputScope="TelephoneNumber"
TextChanged="OnTextBoxTextChanged"
/>
<TextBlock
Grid.Row="2"
Text="text message"
HorizontalAlignment="Left"
Style="{StaticResource
PhoneTextSmallStyle}" />
<TextBlock
Name="charCountText"
Grid.Row="2"
HorizontalAlignment="Right"
Style="{StaticResource
PhoneTextSmallStyle}" />
<TextBox
Name="bodyTextBox"
Grid.Row="3"
MaxLength="160"
TextWrapping="Wrap"
VerticalScrollBarVisibility="Auto"
TextChanged="OnTextBoxTextChanged"
/>
<Button
Name="sendButton"
Grid.Row="4"
Content="send"
IsEnabled="False"
HorizontalAlignment="Center"
Click="OnSendButtonClick"
/>
</Grid>
</Grid>
The first TextBox has its InputScope property set to TelephoneNumber. When
you press on that TextBox, a numeric keypad pops up:
The second doesn't have its InputScope property set so a standard
general-purpose keyboard comes up:
The XAML doesn't show the most important property of the TextBox, which is
the property named Text of type string. At any time, you can programmatically
access the Text property to see what's in there, or you can set the Text
property to initialize the contents. It's also possible to insert something into
the existing contents of the TextBox, or delete something: Get the current Text
property, use normal methods of the String class to create a new string
containing the new text, and then set that string back to the Text
property.
Here's a good chunk of the
MainPage code-behind file:
namespace
OneTimeText
{
public partial
class MainPage
: PhoneApplicationPage
{
PhoneApplicationService appService =
PhoneApplicationService.Current;
SmsComposeTask smsTask;
public MainPage()
{
InitializeComponent();
smsTask = new
SmsComposeTask();
}
void OnTextBoxTextChanged(object
sender, TextChangedEventArgs args)
{
if (sender == bodyTextBox)
charCountText.Text = String.Format("{0}/160",
bodyTextBox.Text.Length);
sendButton.IsEnabled = toTextBox.Text.Length > 0 &&
bodyTextBox.Text.Length > 0;
}
void OnSendButtonClick(object
sender, RoutedEventArgs e)
{
smsTask.To = toTextBox.Text;
smsTask.Body = bodyTextBox.Text;
smsTask.Show();
}
.....
}
}
At some point the user might return to the OneTimeText program. The
SmsComposeTask object doesn't return anything to the program that invoked
it—it's a launcher rather than a chooser—but it would still be nice for the user
to see the text previously entered. For this reason, the program overrides the
OnNavigationFrom and OnNavigationTo methods to save and restore that program
state:
protected
override
void
OnNavigatedFrom(NavigationEventArgs
args)
{
appService.State["toText"]
= toTextBox.Text;
appService.State["bodyText"]
= bodyTextBox.Text;
base.OnNavigatedFrom(args);
}
protected
override
void
OnNavigatedTo(NavigationEventArgs
args)
{
object
text;
if
(appService.State.TryGetValue("toText",
out
text))
toTextBox.Text = text
as
string;
if
(appService.State.TryGetValue("bodyText",
out
text))
bodyTextBox.Text = text
as
string;
base.OnNavigatedTo(args);
}