다음을 통해 공유


Sample: Windows Phone 7 Example Application with Landscape Layout

I’ve had a lot of fun writing Windows Phone 7 applications and for this post I wanted to share some notes and sample source code to help you get started as well.  The latest build of the Windows Phone 7 tools now support Visual Studio 2010 in addition to the free Express SKU.  You can find everything you need for development here.

One of my favorite applications for my phone is a tip calculator which makes it easy to figure out your bill in a restaurant or for a cab ride.  This sample implements the following simple tip calculator:

image

The application has a built-in numeric key pad for entering the bill amount and controls which allow for adjustment of both the tip percentage as well as rounding the total bill to an even number.

Layout

Visual Studio makes it very easy to create an application including styling that matches the ‘Metro’ theme as explained in the Design and Interaction Guide.  The application is arranged in 3 pieces using grids:

image

Each grid is then placed in its corresponding row using the Grid.Row property.

Landscape Mode

When the user rotates the phone we want the layout to change to match the new dimensions.  This is also fairly straight forward to implement.  The first step is to have an additional column in the LayoutRoot which can accommodate the totals data:

image

When the phone is rotated into landscape mode, we will move the TotalsGrid into the second column adjacent to the NumbersGrid.  There are a few things required to make this work.  First LayoutRoot must needs to have 3 rows and 2 columns where content can be placed:

    1: <Grid x:Name="LayoutRoot" Background="{StaticResource PhoneBackgroundBrush}">
    2:     <Grid.RowDefinitions>
    3:         <RowDefinition Height="Auto"/>
    4:         <RowDefinition Height="Auto"/>
    5:         <RowDefinition Height="*"/>
    6:     </Grid.RowDefinitions>
    7:     <Grid.ColumnDefinitions>
    8:         <ColumnDefinition Width="Auto" />
    9:         <ColumnDefinition Width="0" x:Name="LandscapeColumn" />
   10:     </Grid.ColumnDefinitions>

The first two RowDefinition elements have their Height set to “Auto” which means they will consume only as much room as the content they contain.  The final row will take up the open space.  The first column will also use “Auto” for its width.  The second column, LandscapeColumn, is given a width of “0” and will not be used in Portrait mode.  Each grid is placed in its own row by default.

To support landscape and portrait mode, the application must indicate it supports the setting and it must handle the change in orientation event as follows:

    1: SupportedOrientations = SupportedPageOrientation.Portrait | SupportedPageOrientation.Landscape;
    2:  
    3: this.OrientationChanged += new EventHandler<OrientationChangedEventArgs>(MainPage_OrientationChanged);

When the orientation changes we’ll first move the TotalsGrid into the second column adjacent to the NumbersGrid, then change the column width attributes accordingly:

    1: void MainPage_OrientationChanged(object sender, OrientationChangedEventArgs e)
    2: {
    3:     // In landscape mode, the totals grid is moved to the right on the screen
    4:     // which puts it in row 1, column 1.
    5:     if ((e.Orientation & PageOrientation.Landscape) != 0)
    6:     {
    7:         LandscapeColumn.Width = GridLength.Auto;
    8:         Grid.SetRow(TotalsGrid, 1);
    9:         Grid.SetColumn(TotalsGrid, 1);
   10:  
   11:         LayoutRoot.ColumnDefinitions[1].Width = GridLength.Auto;
   12:     }
   13:     // In portrait mode, the totals grid goes below the number pad at the
   14:     // bottom of the screen which is row 0, column 2.
   15:     else
   16:     {
   17:         LandscapeColumn.Width = new GridLength(0);
   18:         Grid.SetRow(TotalsGrid, 2);
   19:         Grid.SetColumn(TotalsGrid, 0);
   20:  
   21:         LayoutRoot.ColumnDefinitions[1].Width = new GridLength(0);
   22:     }
   23: }

The resulting display then looks like the following:

image

Button Handlers

The numeric button grid is created in xaml by placing all items into a 3 x 4 grid.  Each numeric button is given a name “button[x]” which corresponds to its value.  When a button is pressed, we want to add that new digit to the bill amount.   We could go through and add a handler manually using the IDE, but we can do a lot better by taking advantage of the C# anonymous delegates feature:

    1: private void InitButtonHandlers()
    2: {
    3:     for (int i = 0; i <= 9; i++)
    4:     {
    5:         string btnName = "button" + i.ToString();
    6:         System.Windows.Controls.Button btn = 
    7:             (System.Windows.Controls.Button)this.LayoutRoot.FindName(btnName);
    8:  
    9:         int j = i; // avoid local variable capture
   10:         btn.Click += new RoutedEventHandler(
   11:             (object sender, RoutedEventArgs e) => { TryUpdateBillAmount(textBlockBill.Text + j.ToString()); }
   12:         );
   13:     }
   14: }

The loop will find each button using its name and then add a new click handler.  The following is the most interesting piece of this code:

image

The code is defining a button handler for each button instance right in the loop.  The method definition starts with the argument list for a RoutedEventHandler:

(object sender, RoutedEventArgs e)

The lambda syntax, =>, then leads us to the method implementation:

{ TryUpdateBillAmount(textBlockBill.Text + j.ToString()); }

In each case the code takes the current view string value, appends the new digit to the end, and then invokes the TryUpdateBillAmount method which will decide if the update is valid.

The last interesting thing to examine is this line of code:

int j = i; // avoid local variable capture

The local variable “i” is considered an “outer” variable when it gets captured.  This means “i” gets allocated on the heap rather than on the stack and will not contain the per loop value (you will always get the value “10” in this example if you use “i” in the delegate).  In order to get around this gotcha, we declare the local variable “j” in the loop and capture the current value.  (Many thanks to Scott Wiltamuth, co-designer of the C# language specification, for helping me figure this one out!)

Debugging With the Emulator

Debugging is very simple, just press F5!  By default the emulator is your target:

image

As devices become available, you will be able to simply change this value to target your device connected to your computer through USB:

image

You can now test the application easily using the normal Visual Studio debugger and emulator controls.  For example, to test landscape mode simply select the direction you want to rotate the device:

image 

Summary

You can find the full source code for the example here.  The techniques used here are generic Silverlight and C# features which just helps demonstrate how your skill set can easily translate into new form factors like the Phone. 

Enjoy!

Comments

  • Anonymous
    May 25, 2010
    thanks for this sample!

  • Anonymous
    May 25, 2010
    Hi Jason, I'd be interested in a blog post detailing how updates to the new VS2010 will be distributed. 'Check for updates' points you to the usual windows update website. Does this mean fixes/updates are now going to be more frequently distributed or do we still have to wait for service packs?  Roughly when would the first one be released? Regards

  • Anonymous
    May 25, 2010
    did you wrote sample or one of your staff?

  • Anonymous
    May 25, 2010
    Good exemple....

  • Anonymous
    May 25, 2010
    @Cole - thanks for the suggestion, I will make sure to post our process on this. @developer - I try to write all of my own samples based on my own investigations.  I will, of course, send questions when I get stuck and get feedback from the team. It is one method I use to stay in touch with all parts of the product and to give detailed feedback to the teams.  In this case I found our method refactoring dialog doesn't work well with the black background color scheme (bug filed) and I couldn't really find good docs on landscape mode recommendations (which is why I spent a lot more time writing about that piece).  I'm often asked at user groups how to get started with a new area so I've also done a lot of articles around that as well as overviews.  We have some excellent deep subject matter experts with detailed blogs once you get going.

  • Anonymous
    May 26, 2010
    Hey Now Jason, Nice Post! Thx 4 the info, Catto

  • Anonymous
    May 27, 2010
    Thanks for the sample app, ps:Wishing Microsoft the best with this phone, the battle against the iPhone is gonna be a tough one...