ARTICLE

Standard Silverlight Button's for Windows Phone 7

Posted by Charles Petzold Articles | Windows Phone December 09, 2010
The standard Silverlight Button is much more flexible than the ApplicationBar buttons, as well as being easier to use.
Reader Level:
Download Files:
 

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:

ten1.gif

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:

ten2.gif

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>

ten3.gif

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

ten4.gif

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:

ten5.gif

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:

ten6.gif

The second doesn't have its InputScope property set so a standard general-purpose keyboard comes up:

ten7.gif

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);
}

COMMENT USING