After getting over the “Add Service Reference” problem mentioned in the previous post, the Login page was created:

Login Page
XAML file below:
https://vbmagic.wordpress.com/wp-admin/edit.php
<phone:PhoneApplicationPage
x:Class="TyranntPhone.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="480" d:DesignHeight="768"
FontFamily="{StaticResource PhoneFontFamilyNormal}"
FontSize="{StaticResource PhoneFontSizeNormal}"
Foreground="{StaticResource PhoneForegroundBrush}"
SupportedOrientations="PortraitOrLandscape" Orientation="Portrait"
shell:SystemTray.IsVisible="True">
<!--LayoutRoot is the root grid where all page content is placed-->
<Grid x:Name="LayoutRoot" Background="Transparent">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<!--TitlePanel contains the name of the application and page title-->
<StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28">
<TextBlock x:Name="ApplicationTitle" Text="TYRANNT" Style="{StaticResource PhoneTextNormalStyle}"/>
<TextBlock x:Name="PageTitle" Text="login" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/>
</StackPanel>
<!--ContentPanel - place additional content here-->
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<Grid x:Name="LoginGrid" Background="Transparent">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock x:Name="usernameTextBlock" Grid.Row="0" Grid.Column="0" Text="Username" VerticalAlignment="Center" />
<TextBox x:Name="usernameTextBox" Grid.Row="0" Grid.Column="1" Text="" VerticalAlignment="Center" />
<TextBlock x:Name="passwordTextBlock" Grid.Row="1" Grid.Column="0" Text="Password" VerticalAlignment="Center" />
<PasswordBox x:Name="passwordPasswordBox" Grid.Row="1" Grid.Column="1" Password="" VerticalAlignment="Center" IsEnabled="False" />
<CheckBox x:Name="rememberMeCheckBox" Grid.Row="2" Grid.Column="1" Content="Remember Me" IsEnabled="False" />
<Button x:Name="loginButton" Grid.Row="3" Grid.ColumnSpan="2" Content="Login" IsEnabled="False" />
<Button x:Name="logoutButton" Grid.Row="4" Grid.ColumnSpan="2" Content="Logout" Visibility="Collapsed" />
</Grid>
</Grid>
</Grid>
</phone:PhoneApplicationPage>
Then helper methods were created to adjust the page layout and to help with passing information between pages. Also, to help with the application tomb-stoning, the following routines were created in the App.xaml.vb page to store and retrieve information:
Private Sub StoreDetails()
' The PhoneApplicationService stores information while the application is "Active"
Dim phoneAppService As PhoneApplicationService = PhoneApplicationService.Current
' The IsolatedStorageSettings will store informaiton on the phones flash memory to be
' retrieved after the app has shut down and re-started.
Dim settings As IsolatedStorageSettings = IsolatedStorageSettings.ApplicationSettings
' Pull the token GUID from temp storage in the phones memory and write into the
' permanante storage on the phone's flash disk. Only if it exists
Dim token As Guid
If phoneAppService.State.TryGetValue("token", token) = True Then
settings("token") = token
End If
' The same for the remembered username, this will allow the name to be recalled
' after the app has been shut down in any way.
Dim rname As String = ""
If phoneAppService.State.TryGetValue("rememberedUsername", rname) = True Then
settings("rememberedUsername") = rname
End If
' This stores the user group, for the moment, this should only really be done
' if the app is paused to allow another app to run. If the app is exited the
' user will have to log on again anyway.
Dim group As String = ""
If phoneAppService.State.TryGetValue("group", group) = True Then
settings("group") = group
End If
End Sub
Private Sub GetDetails()
' The PhoneApplicationService stores information while the application is "Active"
Dim phoneAppService As PhoneApplicationService = PhoneApplicationService.Current
' The IsolatedStorageSettings will store informaiton on the phones flash memory to be
' retrieved after the app has shut down and re-started.
Dim settings As IsolatedStorageSettings = IsolatedStorageSettings.ApplicationSettings
' The token us used to identify the user without the need for a username and password.
Dim token As Guid
If settings.TryGetValue("token", token) = True Then
phoneAppService.State("token") = token
End If
' if the user has ticked the remember me box, this is the name they used to log on.
Dim rname As String = ""
If settings.TryGetValue("rememberedUsername", rname) = True Then
phoneAppService.State("rememberedUsername") = rname
End If
' this will show if the user is a member of any special user group such as administrator
Dim group As String = ""
If settings.TryGetValue("group", group) = True Then
phoneAppService.State("group") = group
End If
End Sub
I got the idea for this from the WP7 course at http://learnvisualstudio.net a good resource for learning most things .net.
Here is the code to handle the enabling and disabling of sections of the login page:
Partial Public Class MainPage
Inherits PhoneApplicationPage
Private _phoneAppService As PhoneApplicationService = PhoneApplicationService.Current
' Constructor
Public Sub New()
InitializeComponent()
End Sub
Private Sub EnableLogin(ByVal state As Boolean)
usernameTextBox.IsEnabled = state
passwordPasswordBox.IsEnabled = state
loginButton.IsEnabled = state
End Sub
Private Sub usernameTextBox_TextChanged(ByVal sender As System.Object, ByVal e As System.Windows.Controls.TextChangedEventArgs) Handles usernameTextBox.TextChanged
If usernameTextBlock.Text.Length > 3 And passwordPasswordBox.IsEnabled = False Then
passwordPasswordBox.IsEnabled = True
rememberMeCheckBox.IsEnabled = True
ElseIf usernameTextBox.Text.Length <= 3 And passwordPasswordBox.IsEnabled = True Then
passwordPasswordBox.IsEnabled = False
End If
End Sub
Private Sub passwordPasswordBox_PasswordChanged(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles passwordPasswordBox.PasswordChanged
If passwordPasswordBox.Password.Length > 3 And loginButton.IsEnabled = False Then
loginButton.IsEnabled = True
ElseIf passwordPasswordBox.Password.Length <= 3 And loginButton.IsEnabled = True Then
loginButton.IsEnabled = False
End If
End Sub
Private Sub PhoneApplicationPage_Loaded(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles MyBase.Loaded
If RetrieveDetails() = True Then
passwordPasswordBox.Focus()
rememberMeCheckBox.IsChecked = True
Else
usernameTextBox.Focus()
End If
End Sub
Private Sub PhoneApplicationPage_BackKeyPress(ByVal sender As System.Object, ByVal e As System.ComponentModel.CancelEventArgs) Handles MyBase.BackKeyPress
Dim res As MessageBoxResult = MessageBox.Show("Are you sure you wish to exit", "Exit", MessageBoxButton.OKCancel)
If res = MessageBoxResult.Cancel Then
e.Cancel = True
End If
End Sub
Private Sub StoreDetails()
_phoneAppService.State("rememberedUsername") = usernameTextBox.Text
End Sub
Private Function RetrieveDetails() As Boolean
Dim tmp As String = ""
If _phoneAppService.State.TryGetValue("rememberedUsername", tmp) = False Then
Return False
Else
usernameTextBox.Text = tmp
Return True
End If
End Function
Private Sub rememberMeCheckBox_Checked(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles rememberMeCheckBox.Checked
StoreDetails()
End Sub
Private Sub DeleteDetails()
_phoneAppService.State.Remove("rememberedUsername")
End Sub
Private Sub rememberMeCheckBox_Unchecked(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles rememberMeCheckBox.Unchecked
DeleteDetails()
End Sub
End Class
Now that this infrastructure is in place we get to the good bit, consuming the login service. The following routines were created to handle the login button click event and code to handle the call back for the login service.
Private Sub loginButton_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles loginButton.Click
Dim client As New userServiceReference.UserServiceClient
AddHandler client.LoginCompleted, AddressOf client_LoginCompleted
Dim login As New userServiceReference.LoginType
login.Username = usernameTextBox.Text
login.Password = passwordPasswordBox.Password
client.LoginAsync(login)
EnableLogin(False)
If rememberMeCheckBox.IsChecked = True Then
StoreDetails()
End If
End Sub
Private Sub client_LoginCompleted(ByVal sender As Object, ByVal e As userServiceReference.LoginCompletedEventArgs)
If e.Error Is Nothing Then
Dim res As userServiceReference.TokenType = e.Result
If res.errorMessage <> "" And res.errorMessage <> "fail" Then
MessageBox.Show(res.errorMessage)
EnableLogin(True)
ElseIf res.errorMessage = "fail" Then
MessageBox.Show("Username/Password incorrect")
EnableLogin(True)
Else
_phoneAppService.State("token") = res.TokenGUID
_phoneAppService.State("group") = res.UserGroup
NavigationService.Navigate(New Uri("/pages/MemberPivotPage.xaml", UriKind.Relative))
loginButton.Visibility = Windows.Visibility.Collapsed
logoutButton.Visibility = Windows.Visibility.Visible
End If
Else
MessageBox.Show(e.Error.Message)
EnableLogin(True)
End If
End Sub
The contents of the username and password from the boxes on the page are used to generate a login type which is defined in the web service but I’ll re-show here:
<DataContract()>
Public Class LoginType
<DataMember()>
Public Property Username() As String
<DataMember()>
Public Property Password() As String
End Class
Then the callback routine to handle the completed login web service call was created and an event handler put into place. Then the ASYNC version of the service (Which is the only one available on Silverlight for Windows Phone) is called and the UI is changed to stop the user from trying to log in twice.
At the server end, the Membership service will attempt to validate the credentials. (A side note: To add a new user, you need to register on the website. Which is all handled automatically if you create an MVC3 website rather than a blank project.)
When the service returns the result of the login, firstly the app checks to see if the e.Error object has been set, this will indicate something went wrong with the service call and for now, it will just display the error message in a message box on the phone (Example of this is a timeout). Next the app will have a look at the returned object which is a TokenType which again is defined in the web service and I’ve shown again below:
<DataContract()>
Public Class TokenType
<DataMember()>
Public Property UserGroup() As String
<DataMember()>
Public Property TokenGUID() As Guid
<DataMember()>
Public Property errorMessage() As String
End Class
The errorMessage property of the TokenType class is used to pass back in the login failed for a username/password error. Basically if errorMessage is “” then everything went OK. If it contains the text “fail” then the username/password combination isn’t valid so the phone app will display that in a message box and reset the display to allow the user to attempt to log on again. If an exception occurred during the login process this is captured by the service and put into the errorMessage property. The phone app will check this and for now display that error message to aid in debugging.
If everything went well, then there should be a GUID in the TokenGUID property which will be used from that point onwards to identify that user. Later I will make every attempt to access the services change this GUID and pass the new one back. I will also add a time out so that the app can get the user to re-login after a period of inactivity has occurred.
If the login was successful we then enable a Logout button and collapse the login button and navigate to the “Member” section of the app using the following command:
NavigationService.Navigate(New Uri("/pages/MemberPivotPage.xaml", UriKind.Relative))
Next post on this experiment will be about what happens on this page.