Windows Phone applications are generally created using either HTML5 or Silverlight. Most of the people still use the Silverlight approach as it has a full flavor of backend languages such as C# and also the JavaScript library is still in its infancy. With Silverlight or XAML, the architecture that always comes into the developer's mind is MVVM. Like all XAML-based development, Windows 8 Silverlight apps also inherently support MVVM models and hence, people tend to adopt it more often when developing Windows Phone apps. In this recipe, we are going to take a quick look at how you can use the MVVM pattern to implement an application.
Before starting to develop an application, you first need to set up your machine with the appropriate SDK, which lets you develop a Windows Phone application and also gives you an emulator to debug the application without a device. The SDK for Windows Phone 8 apps can be downloaded from Windows Phone Dev Center at http://dev.windowsphone.com. The Windows Phone SDK includes the following:
After everything has been set up for application development, you can open Visual Studio and create a Windows Phone app. When you create the project, it will first ask the target platform; choose Windows Phone 8 as the default and select OK. You need to name and create the project.
Now that the template is created, let's follow these steps to demonstrate how we can start creating an application:
MainPage.xaml
file should already be loaded with a lot of initial adjustments to support Windows Phone with factors. Microsoft makes sure that they give the best layout to the developer to start with. So the important thing that you need to look at is defining the content inside the ContentPanel
property, which represents the workspace area of the page.Login.xaml
, and add the following code in ContentPanel
defined inside the page:<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0" VerticalAlignment="Center"> <Grid.RowDefinitions> <RowDefinition Height="Auto"/> <RowDefinition Height="Auto" /> <RowDefinition Height="Auto" /> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition /> <ColumnDefinition /> </Grid.ColumnDefinitions> <TextBlock Text="UserId" Grid.Row="0" Grid.Column="0" HorizontalAlignment="Right" VerticalAlignment="Center"/> <TextBox Text="{Binding UserId, Mode=TwoWay}" Grid.Row="0" Grid.Column="1" InputScope="Text"/> <TextBlock Text="Password" Grid.Row="1" Grid.Column="0" HorizontalAlignment="Right" VerticalAlignment="Center"/> <PasswordBox x:Name="txtPassword" Grid.Row="1" Grid.Column="1" PasswordChanged="txtPassword_PasswordChanged"/> <Button Command="{Binding LoginCommand}" Content="Login" Grid.Row="2" Grid.Column="0" /> <Button Command="{Binding ClearCommand}" Content="Clear" Grid.Row="2" Grid.Column="1" /> </Grid>
In the preceding UI Design, we added a TextBox
and a PasswordBox
inside ContentPanel
. Each TextBox
has an InputScope
property, which you can define to specify the behavior of the input. We define it as Text
, which specifies that the TextBox
can have any textual data. The PasswordBox
takes any input from the user, but shows asterisks (*
) instead of the actual data. The actual data is stored in an encrypted format inside the control and can only be recovered using its Password
property.
Model
in the solution and put a LoginDataContext
class in it. This class is used to generate and validate the login of the UI.INotifyPropertyChanged
, which indicates that the properties can act by binding with the corresponding DependencyProperty
that exists in the control, thereby interacting to and fro with the UI.UserName
, Password
, and Status
, as shown in the following code:private string userid; public string UserId { get { return userid; } set { UserId = value; this.OnPropertyChanged("UserId"); } } private string password; public string Password { get { return password; } set { password = value; this.OnPropertyChanged("Password"); } } public bool Status { get; set; }
You can see in the preceding code that the property setter invokes an OnPropertyChanged
event. This ensures that the update on the properties is reflected in the UI control:
public ICommand LoginCommand { get { return new RelayCommand((e) => { this.Status = this.UserId == "Abhishek" && this.Password == "winphone"; if (this.Status) { var rootframe = App.Current.RootVisual as PhoneApplicationFrame; rootframe.Navigate(new Uri(string.Format("/FirstPhoneApp;component/MainPage.xaml?name={0}", this.UserId), UriKind.Relative)); } }); } } public ICommand ClearCommand { get { return new RelayCommand((e) => { this.UserId = this.Password = string.Empty; }); } }
We also define two more properties of type ICommand
. The UI button control implements the command pattern and uses an ICommand
interface to invoke a command. The RelayCommand
used on the code is an implementation of the ICommand
interface, which could be used to invoke some action.
Text
property of the TextBox
in XAML with the UserId
property, and make it a TwoWay
binding. The binder automatically subscribes to the PropertyChanged
event. When the UserId
property is set and the PropertyChanged
event is invoked, the UI automatically receives the invoke request of code, which updates the text in the UI.Login
and Clear
and bind them with the properties LoginCommand
and ClearCommand
, as shown in the following code:<Button Command="{Binding LoginCommand}" Content="Login" Grid.Row="2" Grid.Column="0" /> <Button Command="{Binding ClearCommand}" Content="Clear" Grid.Row="2" Grid.Column="1" />
In the preceding XAML, we defined the two buttons and specified a command for each of them.
MainPage.xaml
file as follows:<StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28"> <TextBlock Text="MY APPLICATION" x:Name="txtApplicationDescription" Style="{StaticResource PhoneTextNormalStyle}" Margin="12,0"/> <TextBlock Text="Enter Details" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/> </StackPanel>
We add the preceding XAML to show the message that is passed from the Login screen.
MainDataContext
. We define a property that will hold the data to be displayed on the screen.Login.xaml.cs
created as a code-behind of Login.xaml
, create an instance of LoginDataContext
, and assign it to DataContext
of the page. We assign this inside the InitializeComponent
method of the class, as shown in the following code:this.DataContext = new LoginDataContext();
WMAppManifest
file, and specify Login.xaml
as the Navigation page. Once this is done, if you run the application now in any of the emulators available with Visual Studio, you will see what is shown in the following screenshot:You can enter data in the UserId and Password fields and click on Login, but nothing happens.
LoginCommand
and press Login again with the credentials, and you will see that the Password
property is never set to anything and evaluates to null. Note that, PasswordBox
in XAML does not support binding to its properties. To deal with this, we define a PasswordChanged
event on PasswordBox
and specify the following code:private void txtPassword_PasswordChanged(object sender, RoutedEventArgs e) { this.logindataContext.Password = txtPassword.Password; }
The preceding code will ensure that the password goes properly to the ViewModel
.
true
.MainPage.xaml
. To do this, we change the LoginCommand
property to navigate the page, as shown in the following code:if (this.Status) { var rootframe = App.Current.RootVisual as PhoneApplicationFrame; rootframe.Navigate(new Uri(string.Format("/FirstPhoneApp;component/MainPage.xaml?name={0}", this.UserId), UriKind.Relative)); }
Each WPF app contains an ApplicationFrame
class that is used to show the UI. The application frame can use the navigate
method to navigate from one page to another. The navigate
method uses NavigationService
to redirect the page to the URL provided. Here in the code, after authentication, we pass UserId
as querystring
to MainPage
.
MainPage.xaml
file to include a pivot control. A pivot control is just like traditional tab controls, but looks awesome in a phone environment. Let's add the following code:<phone:Pivot> <phone:PivotItem Header="Main"> <StackPanel Orientation="Vertical"> <TextBlock Text="Choose your avatar" /> <Image x:Name="imgSelection" Source="{Binding AvatarImage}"/> <Button x:Name="btnChoosePhoto" ClickMode="Release" Content="Choose Photo" Command="{Binding ChoosePhoto}" /> </StackPanel> </phone:PivotItem> <phone:PivotItem Header="Task"> <StackPanel> <phone:LongListSelector ItemsSource="{Binding LongList}" /> </StackPanel> </phone:PivotItem> </phone:Pivot>
phone
tag is referred to a namespace that has been added automatically in the header where the Pivot
class exists. In the previously defined Pivot
class, there are two PivotItem
with headers Main
and Task
. When Main
is selected, it allows you to choose a photo from MediaLibrary
and the image is displayed on Image Control. The ChoosePhoto
command defined inside MainDataContext
sets the image to its source, as shown in the following code:public ICommand ChoosePhoto { get { return new RelayCommand((e) => { PhotoChooserTask pTask = new PhotoChooserTask(); pTask.Completed += pTask_Completed; pTask.Show(); }); } } void pTask_Completed(object sender, PhotoResult e) { if (e.TaskResult == TaskResult.OK) { var bitmap = new BitmapImage(); bitmap.SetSource(e.ChosenPhoto); this.AvatarImage = bitmap; } }
In the preceding code, the RelayCommand
that is invoked when the button is clicked uses PhotoChooserTask
to select an image from MediaLibrary
and that image is shown on the AvatarImage
property bound to the image source.
PivotItem
shows LongList
where the ItemsSource
is bound to a long list of strings, as shown in the following code:public List<string> LongList { get { this.longList = this.longList ?? this.LoadList(); return this.longList; } }
The long list can be anything, a long list that is needed to be shown in the ListBox
class.
Windows Phone, being an XAML-based technology, uses Silverlight to generate UI and controls supporting the Model-View-ViewModel (MVVM) pattern. Each of the controls present in the Windows Phone environment implements a number of DependencyProperties
. The DependencyProperty
is a special type of property that supports DataBinding
. When bound to another CLR object, these properties try to find the INotifyPropertyChanged
interface and subscribe to the PropertyChanged
event. When the data is modified in the controls, the actual bound object gets modified automatically by the dependency property system, and vice versa.
Similar to normal DependencyProperties
, there is a Command
property that allows you to call a method. Just like the normal property, Command
implements the ICommand
interface and has a return type Action
that maps to Command
. The RelayCommand
here is an implementation of ICommand
interfaces, which can be bound to the Command
property of Button
.
Now let's talk about some other options, or possibly some pieces of general information that are relevant to this task.
Just like any of the modern smartphones, Windows Phones also provides a standard way of communicating with the environment. Each application can have a standard set of icons at the bottom of the application, which enable the user to perform some actions on the application. The ApplicationBar
class is present at the bottom of any application across the operating system and hence, people tend to expect commands to be placed on ApplicationBar
rather than on the application itself, as shown in the following screenshot. The ApplicationBar
class accepts 72 pixels of height, which cannot be modified by code.
When an application is open, the application bar is shown at the bottom of the screen. The preceding screenshot shows how the ApplicationBar
class is laid out with two buttons, login and clear. Each ApplicationBar
class can also associate a number of menu items for additional commands. The menu could be opened by clicking on the … button in the left-hand side of ApplicationBar
.
The page of Windows Phone allows you to define one application bar. There is a property called ApplicationBar
on PhoneApplicationPage
that lets you define the ApplicationBar
class of that particular page, as shown in the following screenshot:
<phone:PhoneApplicationPage.ApplicationBar> <shell:ApplicationBar> <shell:ApplicationBarIconButton Click="ApplicationBarIconButton_Click" Text="Login" IconUri="/Assets/next.png"/> <shell:ApplicationBarIconButton Click="ApplicationBarIconButtonSave_Click" Text="clear" IconUri="/Assets/delete.png"/> <shell:ApplicationBar.MenuItems> <shell:ApplicationBarMenuItem Click="about_Click" Text="about" /> </shell:ApplicationBar.MenuItems> </shell:ApplicationBar> </phone:PhoneApplicationPage.ApplicationBar>
In the preceding code, we defined two ApplicationBarIconButton
classes. Each of them defines the Command
items placed on the ApplicationBar
class. The ApplicationBar
.MenuItems
method allows us to add menu items to the application. There can be a maximum of four application bar buttons and four menus per page. The ApplicationBar
button also follows a special type of icon. There are a number of these icons added with the SDK, which could be used for the application. They can be found at DriveNameProgram FilesMicrosoft SDKs Windows Phonev8.0Icons
.
There are separate folders for both dark and light themes. It should be noted that ApplicationBar
buttons do not allow command bindings.
When dealing with Windows Phone applications, there are some special things to consider. When a user navigates out of the application, the application is transferred to a dormant state, where all the pages and state of the pages are still in memory but their execution is totally stopped. When the user navigates back to the application again, the state of the application is resumed and the application is again activated. Sometimes, it might also be possible that the app gets tombstoned after the user navigates away from the app. In this case, the app is not preserved in memory, but some information of the app is stored. Once the user comes back to the app, the application needs to be restored, and the application needs to resume in such a way that the user gets the same state as he or she left it. In the following figure, you can see the entire process:
There are four states defined, the first one is the Not Running state where there is no existence of the process in memory. The Activated state is when the app is tapped by the user. When the user moves out of the app, it goes from Suspending to Suspended. It can be reactivated or it will be terminated after a certain time automatically.
Let's look at the Login screen, where you might sometimes tombstone the login page while entering the user ID and password. To deal with storing the user state data before tombstoning, we use PhoneApplicationPage
. The idea is to serialize the whole DataModel
once the user navigates away from the page and retrieves the page state again when it navigates back.
Let's annotate the UserId
and Password
of the LoginDataContext
with DataMember
and LoginDataContext
with DataContract
, as shown in the following code:
[DataContract] public class LoginDataContext : PropertyBase { private string userid; [DataMember] public string UserId { get { return userid; } set { UserId = value; this.OnPropertyChanged("UserId"); } } private string password; [DataMember] public string Password { get { return password; } set { password = value; this.OnPropertyChanged("Password"); } } }
The DataMember
property will indicate that the properties are capable of serializing. As the user types into these properties, the properties get filled with data so that when the user navigates away, the model will always have the latest data present.
In LoginPage
, we define a property called _isNewPageInstance
and set it to false
, and in constructor, we set it to true
. This will indicate that only when the page is instantiated, _isNewPageInstance
is set to true
.
Now, when the user navigates away from the page, OnNavigatedFrom
gets called. If the user navigates from the page, we save ViewModel
into State
as shown in the following code:
protected override void OnNavigatedFrom(NavigationEventArgs e) { base.OnNavigatedFrom(e); if (e.NavigationMode != System.Windows.Navigation.NavigationMode.Back) { // Save the ViewModel variable in the page''s State dictionary. State[""ViewModel""] = logindataContext; } }
Once DataModel
is saved in the State
object, it is persistent and can be retrieved later on when the application is resumed as follows:
protected override void OnNavigatedTo(NavigationEventArgs e) { base.OnNavigatedTo(e); if (_isNewPageInstance) { if (this.logindataContext == null) { if (State.Count > 0) { this.logindataContext = (LoginDataContext)State[""ViewModel""]; } else { this.logindataContext = new LoginDataContext(); } } DataContext = this.logindataContext; } _isNewPageInstance = false; }
When the application is resumed from tombstoning, it calls OnNavigatedTo
and retrieves DataModel
back from the state.
52.15.55.18