This sample shows how to connect a Windows Phone 8.0 app to Facebook, Google and Microsoft accounts. The main features: Login/Logout and an about page with feedback, share in social networks, review and share by email.

Download C# (19.9 MB)

Introduction

This sample shows how to connect a Windows Phone 8.0 app to Facebook, Google and Microsoft account.

Main Features: Login/Logout and an about page with feedback, share in social networks, review and share by email.

Building the Sample

You only need Visual Studio 2012/Visual Studio 2013 and Windows 8/Windows 8.1, both the RTM version.

This sample requires the installation for Live SDK (Downloads).

Description

This sample shows how to connect a Windows Phone 8.0 app to Facebook, Google and Microsoft accounts.

Main Features

Note: This sample uses MVVM Light and Cimbalino Windows Phone Toolkit.

For this sample the following was used:

For each provider it is necessry to get the app id/client id/client secret in their websites.

For Google go to https://console.developers.google.com/project and create a new project (APIs & auth > credentials).

Installed

For Facebook go to Facebook Developers and create a new app.

For Live SDK go to Sign in and create one or use an existing app.

Before you start you should change the Constant file to add client ids / client secret / app id, without it the app fails!!

This file is inside the Resource folder.

C#

  1. /// <summary>
  2. /// Defines the constants strings used in the app.
  3. /// </summary>
  4. public class Constants
  5. {
  6. /// <summary>
  7. /// The facebook app id.
  8. /// </summary>
  9. public const string FacebookAppId = "<app id>";
  10. /// <summary>
  11. /// The google client identifier.
  12. /// </summary>
  13. public const string GoogleClientId = "<client id>";
  14. /// <summary>
  15. /// The google token file name.
  16. /// </summary>
  17. public const string GoogleTokenFileName = "Google.Apis.Auth.OAuth2.Responses.TokenResponse-user";
  18. /// <summary>
  19. /// The google client secret.
  20. /// </summary>
  21. public const string GoogleClientSecret = "<client secret>";
  22. /// <summary>
  23. /// The microsoft client identifier.
  24. /// </summary>
  25. public const string MicrosoftClientId = "<client id>";
  26. ...
  27. }

Now let's see how to connect to each provider. For help, I created a SessionService that managed the Login and Logout using a provider value, this is nice because in LoginView I set the buttons to the same command and for each command I set the provider in commandparameter. With it the LoginView and LoginViewModel are clearer and simpler. Another thing is for example if I need to connect to my server to accept the user I can do it in the session manager after the authentication, without adding the code to each provider.

The classes created:

The FacebookService is:

C#


  1. /// <summary>
  2. /// Defines the Facebook Service.
  3. /// </summary>
  4. public class FacebookService : IFacebookService
  5. {
  6. private readonly ILogManager _logManager;
  7. private readonly FacebookSessionClient _facebookSessionClient;
  8. /// <summary>
  9. /// Initializes a new instance of the <see cref="FacebookService"/> class.
  10. /// </summary>
  11. /// <param name="logManager">
  12. /// The log manager.
  13. /// </param>
  14. public FacebookService(ILogManager logManager)
  15. {
  16. _logManager = logManager;
  17. _facebookSessionClient = new FacebookSessionClient(Constants.FacebookAppId);
  18. }
  19. /// <summary>
  20. /// The login sync.
  21. /// </summary>
  22. /// <returns>
  23. /// The <see cref="Task"/> object.
  24. /// </returns>
  25. public async Task<Session> LoginAsync()
  26. {
  27. Exception exception;
  28. Session sessionToReturn = null;
  29. try
  30. {
  31. var session = await _facebookSessionClient.LoginAsync("user_about_me,read_stream");
  32. sessionToReturn = new Session
  33. {
  34. AccessToken = session.AccessToken,
  35. Id = session.FacebookId,
  36. ExpireDate = session.Expires,
  37. Provider = Constants.FacebookProvider
  38. };
  39. return sessionToReturn;
  40. }
  41. catch (InvalidOperationException)
  42. {
  43. throw;
  44. }
  45. catch (Exception ex)
  46. {
  47. exception = ex;
  48. }
  49. await _logManager.LogAsync(exception);
  50. return sessionToReturn;
  51. }
  52. /// <summary>
  53. /// Logouts this instance.
  54. /// </summary>
  55. public async void Logout()
  56. {
  57. Exception exception = null;
  58. try
  59. {
  60. _facebookSessionClient.Logout();
  61. // clean all cookies from browser, is a workarround
  62. await new WebBrowser().ClearCookiesAsync();
  63. }
  64. catch (Exception ex)
  65. {
  66. exception = ex;
  67. }
  68. if (exception != null)
  69. {
  70. await _logManager.LogAsync(exception);
  71. }
  72. }
  73. }

Note: In logout I added a workarround to clear all cookies in browser, if I don´t this in the first time you can login with account you want but in the next time it will use the account used in last login.

The GoogleService is:

C#

  1. /// <summary>
  2. /// The google service.
  3. /// </summary>
  4. public class GoogleService : IGoogleService
  5. {
  6. private readonly ILogManager _logManager;
  7. private readonly IStorageService _storageService;
  8. private UserCredential _credential;
  9. private Oauth2Service _authService;
  10. private Userinfoplus _userinfoplus;
  11. /// <summary>
  12. /// Initializes a new instance of the <see cref="GoogleService" /> class.
  13. /// </summary>
  14. /// <param name="logManager">The log manager.</param>
  15. /// <param name="storageService">The storage service.</param>
  16. public GoogleService(ILogManager logManager, IStorageService storageService)
  17. {
  18. _logManager = logManager;
  19. _storageService = storageService;
  20. }
  21. /// <summary>
  22. /// The login async.
  23. /// </summary>
  24. /// <returns>
  25. /// The <see cref="Task"/> object.
  26. /// </returns>
  27. public async Task<Session> LoginAsync()
  28. {
  29. Exception exception = null;
  30. try
  31. {
  32. // Oauth2Service.Scope.UserinfoEmail
  33. _credential = await GoogleWebAuthorizationBroker.AuthorizeAsync(new ClientSecrets
  34. {
  35. ClientId = Constants.GoogleClientId,
  36. ClientSecret = Constants.GoogleClientSecret
  37. }, new[] { Oauth2Service.Scope.UserinfoProfile }, "user", CancellationToken.None);
  38. var session = new Session
  39. {
  40. AccessToken = _credential.Token.AccessToken,
  41. Provider = Constants.GoogleProvider,
  42. ExpireDate =
  43. _credential.Token.ExpiresInSeconds != null
  44. ? new DateTime(_credential.Token.ExpiresInSeconds.Value)
  45. : DateTime.Now.AddYears(1),
  46. Id = string.Empty
  47. };
  48. return session;
  49. }
  50. catch (TaskCanceledException taskCanceledException)
  51. {
  52. throw new InvalidOperationException("Login canceled.", taskCanceledException);
  53. }
  54. catch (Exception ex)
  55. {
  56. exception = ex;
  57. }
  58. await _logManager.LogAsync(exception);
  59. return null;
  60. }
  61. /// <summary>
  62. /// Gets the user information.
  63. /// </summary>
  64. /// <returns>
  65. /// The user info.
  66. /// </returns>
  67. public async Task<Userinfoplus> GetUserInfo()
  68. {
  69. _authService = new Oauth2Service(new BaseClientService.Initializer()
  70. {
  71. HttpClientInitializer = _credential,
  72. ApplicationName = AppResources.ApplicationTitle,
  73. });
  74. _userinfoplus = await _authService.Userinfo.V2.Me.Get().ExecuteAsync();
  75. return _userinfoplus;
  76. }
  77. /// <summary>
  78. /// The logout.
  79. /// </summary>
  80. public async void Logout()
  81. {
  82. await new WebBrowser().ClearCookiesAsync();
  83. if (_storageService.FileExists(Constants.GoogleTokenFileName))
  84. {
  85. _storageService.DeleteFile(Constants.GoogleTokenFileName);
  86. }
  87. }
  88. }

Note: In the logout for the Google provider there isn´t a logout method, the solution is to remove all cookies and remove the file created in the login operation.

The MicrosoftService is:

C#

  1. /// <summary>
  2. /// The microsoft service.
  3. /// </summary>
  4. public class MicrosoftService : IMicrosoftService
  5. {
  6. private readonly ILogManager _logManager;
  7. private LiveAuthClient _authClient;
  8. private LiveConnectSession _liveSession;
  9. /// <summary>
  10. /// Defines the scopes the application needs.
  11. /// </summary>
  12. private static readonly string[] Scopes = { "wl.signin", "wl.basic", "wl.offline_access" };
  13. /// <summary>
  14. /// Initializes a new instance of the <see cref="MicrosoftService"/> class.
  15. /// </summary>
  16. /// <param name="logManager">
  17. /// The log manager.
  18. /// </param>
  19. public MicrosoftService(ILogManager logManager)
  20. {
  21. _logManager = logManager;
  22. }
  23. /// <summary>
  24. /// The login async.
  25. /// </summary>
  26. /// <returns>
  27. /// The <see cref="Task"/> object.
  28. /// </returns>
  29. public async Task<Session> LoginAsync()
  30. {
  31. Exception exception = null;
  32. try
  33. {
  34. _authClient = new LiveAuthClient(Constants.MicrosoftClientId);
  35. var loginResult = await _authClient.InitializeAsync(Scopes);
  36. var result = await _authClient.LoginAsync(Scopes);
  37. if (result.Status == LiveConnectSessionStatus.Connected)
  38. {
  39. _liveSession = loginResult.Session;
  40. var session = new Session
  41. {
  42. AccessToken = result.Session.AccessToken,
  43. ExpireDate = result.Session.Expires.DateTime,
  44. Provider = Constants.MicrosoftProvider,
  45. };
  46. return session;
  47. }
  48. }
  49. catch (LiveAuthException ex)
  50. {
  51. throw new InvalidOperationException("Login canceled.", ex);
  52. }
  53. catch (Exception e)
  54. {
  55. exception = e;
  56. }
  57. await _logManager.LogAsync(exception);
  58. return null;
  59. }
  60. /// <summary>
  61. /// The logout.
  62. /// </summary>
  63. public async void Logout()
  64. {
  65. if (_authClient == null)
  66. {
  67. _authClient = new LiveAuthClient(Constants.MicrosoftClientId);
  68. var loginResult = await _authClient.InitializeAsync(Scopes);
  69. }
  70. _authClient.Logout();
  71. }
  72. }

The SessionService is:

C#

  1. /// <summary>
  2. /// The service session.
  3. /// </summary>
  4. public class SessionService : ISessionService
  5. {
  6. private readonly IApplicationSettingsService _applicationSettings;
  7. private readonly IFacebookService _facebookService;
  8. private readonly IMicrosoftService _microsoftService;
  9. private readonly IGoogleService _googleService;
  10. private readonly ILogManager _logManager;
  11. /// <summary>
  12. /// Initializes a new instance of the <see cref="SessionService" /> class.
  13. /// </summary>
  14. /// <param name="applicationSettings">The application settings.</param>
  15. /// <param name="facebookService">The facebook service.</param>
  16. /// <param name="microsoftService">The microsoft service.</param>
  17. /// <param name="googleService">The google service.</param>
  18. /// <param name="logManager">The log manager.</param>
  19. public SessionService(IApplicationSettingsService applicationSettings,
  20. IFacebookService facebookService,
  21. IMicrosoftService microsoftService,
  22. IGoogleService googleService, ILogManager logManager)
  23. {
  24. _applicationSettings = applicationSettings;
  25. _facebookService = facebookService;
  26. _microsoftService = microsoftService;
  27. _googleService = googleService;
  28. _logManager = logManager;
  29. }
  30. /// <summary>
  31. /// Gets the session.
  32. /// </summary>
  33. /// <returns>The session object.</returns>
  34. public Session GetSession()
  35. {
  36. var expiryValue = DateTime.MinValue;
  37. string expiryTicks = LoadEncryptedSettingValue("session_expiredate");
  38. if (!string.IsNullOrWhiteSpace(expiryTicks))
  39. {
  40. long expiryTicksValue;
  41. if (long.TryParse(expiryTicks, out expiryTicksValue))
  42. {
  43. expiryValue = new DateTime(expiryTicksValue);
  44. }
  45. }
  46. var session = new Session
  47. {
  48. AccessToken = LoadEncryptedSettingValue("session_token"),
  49. Id = LoadEncryptedSettingValue("session_id"),
  50. ExpireDate = expiryValue,
  51. Provider = LoadEncryptedSettingValue("session_provider")
  52. };
  53. _applicationSettings.Set(Constants.LoginToken, true);
  54. _applicationSettings.Save();
  55. return session;
  56. }
  57. /// <summary>
  58. /// The save session.
  59. /// </summary>
  60. /// <param name="session">
  61. /// The session.
  62. /// </param>
  63. private void Save(Session session)
  64. {
  65. SaveEncryptedSettingValue("session_token", session.AccessToken);
  66. SaveEncryptedSettingValue("session_id", session.Id);
  67. SaveEncryptedSettingValue("session_expiredate", session.ExpireDate.Ticks.ToString(CultureInfo.InvariantCulture));
  68. SaveEncryptedSettingValue("session_provider", session.Provider);
  69. _applicationSettings.Set(Constants.LoginToken, true);
  70. _applicationSettings.Save();
  71. }
  72. /// <summary>
  73. /// The clean session.
  74. /// </summary>
  75. private void CleanSession()
  76. {
  77. _applicationSettings.Reset("session_token");
  78. _applicationSettings.Reset("session_id");
  79. _applicationSettings.Reset("session_expiredate");
  80. _applicationSettings.Reset("session_provider");
  81. _applicationSettings.Reset(Constants.LoginToken);
  82. _applicationSettings.Save();
  83. }
  84. /// <summary>
  85. /// The login async.
  86. /// </summary>
  87. /// <param name="provider">
  88. /// The provider.
  89. /// </param>
  90. /// <returns>
  91. /// The <see cref="Task"/> object.
  92. /// </returns>
  93. public async Task<bool> LoginAsync(string provider)
  94. {
  95. Exception exception = null;
  96. try
  97. {
  98. Session session = null;
  99. switch (provider)
  100. {
  101. case Constants.FacebookProvider:
  102. session = await _facebookService.LoginAsync();
  103. break;
  104. case Constants.MicrosoftProvider:
  105. session = await _microsoftService.LoginAsync();
  106. break;
  107. case Constants.GoogleProvider:
  108. session = await _googleService.LoginAsync();
  109. break;
  110. }
  111. if (session != null)
  112. {
  113. Save(session);
  114. }
  115. return true;
  116. }
  117. catch (InvalidOperationException e)
  118. {
  119. throw;
  120. }
  121. catch (Exception ex)
  122. {
  123. exception = ex;
  124. }
  125. await _logManager.LogAsync(exception);
  126. return false;
  127. }
  128. /// <summary>
  129. /// The logout.
  130. /// </summary>
  131. public async void Logout()
  132. {
  133. Exception exception = null;
  134. try
  135. {
  136. var session = GetSession();
  137. switch (session.Provider)
  138. {
  139. case Constants.FacebookProvider:
  140. _facebookService.Logout();
  141. break;
  142. case Constants.MicrosoftProvider:
  143. _microsoftService.Logout();
  144. break;
  145. case Constants.GoogleProvider:
  146. _googleService.Logout();
  147. break;
  148. }
  149. CleanSession();
  150. }
  151. catch (Exception ex)
  152. {
  153. exception = ex;
  154. }
  155. if (exception != null)
  156. {
  157. await _logManager.LogAsync(exception);
  158. }
  159. }
  160. /// <summary>
  161. /// Loads an encrypted setting value for a given key.
  162. /// </summary>
  163. /// <param name="key">
  164. /// The key to load.
  165. /// </param>
  166. /// <returns>
  167. /// The value of the key.
  168. /// </returns>
  169. private string LoadEncryptedSettingValue(string key)
  170. {
  171. string value = null;
  172. var protectedBytes = _applicationSettings.Get<byte[]>(key);
  173. if (protectedBytes != null)
  174. {
  175. byte[] valueBytes = ProtectedData.Unprotect(protectedBytes, null);
  176. value = Encoding.UTF8.GetString(valueBytes, 0, valueBytes.Length);
  177. }
  178. return value;
  179. }
  180. /// <summary>
  181. /// Saves a setting value against a given key, encrypted.
  182. /// </summary>
  183. /// <param name="key">
  184. /// The key to save against.
  185. /// </param>
  186. /// <param name="value">
  187. /// The value to save against.
  188. /// </param>
  189. /// <exception cref="System.ArgumentOutOfRangeException">
  190. /// The key or value provided is unexpected.
  191. /// </exception>
  192. private void SaveEncryptedSettingValue(string key, string value)
  193. {
  194. if (!string.IsNullOrWhiteSpace(key) && !string.IsNullOrWhiteSpace(value))
  195. {
  196. byte[] valueBytes = Encoding.UTF8.GetBytes(value);
  197. // Encrypt the value by using the Protect() method.
  198. byte[] protectedBytes = ProtectedData.Protect(valueBytes, null);
  199. _applicationSettings.Set(key, protectedBytes);
  200. _applicationSettings.Save();
  201. }
  202. }
  203. }

Now is time to build the User Interface, and because I am using MVVM, I created a LoginViewModel to bind to the LoginView.

The LoginViewModel is:

C#

  1. /// <summary>
  2. /// The login view model.
  3. /// </summary>
  4. public class LoginViewModel : ViewModelBase
  5. {
  6. private readonly ILogManager _logManager;
  7. private readonly IMessageBoxService _messageBox;
  8. private readonly INavigationService _navigationService;
  9. private readonly ISessionService _sessionService;
  10. private bool _inProgress;
  11. /// <summary>
  12. /// Initializes a new instance of the <see cref="LoginViewModel"/> class.
  13. /// </summary>
  14. /// <param name="navigationService">
  15. /// The navigation service.
  16. /// </param>
  17. /// <param name="sessionService">
  18. /// The session service.
  19. /// </param>
  20. /// <param name="messageBox">
  21. /// The message box.
  22. /// </param>
  23. /// <param name="logManager">
  24. /// The log manager.
  25. /// </param>
  26. public LoginViewModel(INavigationService navigationService,
  27. ISessionService sessionService,
  28. IMessageBoxService messageBox,
  29. ILogManager logManager)
  30. {
  31. _navigationService = navigationService;
  32. _sessionService = sessionService;
  33. _messageBox = messageBox;
  34. _logManager = logManager;
  35. LoginCommand = new RelayCommand<string>(LoginAction);
  36. }
  37. /// <summary>
  38. /// Gets or sets a value indicating whether in progress.
  39. /// </summary>
  40. /// <value>
  41. /// The in progress.
  42. /// </value>
  43. public bool InProgress
  44. {
  45. get { return _inProgress; }
  46. set { Set(() => InProgress, ref _inProgress, value); }
  47. }

  48. /// <summary>
  49. /// Gets the facebook login command.
  50. /// </summary>
  51. /// <value>
  52. /// The facebook login command.
  53. /// </value>
  54. public ICommand LoginCommand { get; private set; }

  55. /// <summary>
  56. /// Facebook's login action.
  57. /// </summary>
  58. /// <param name="provider">
  59. /// The provider.
  60. /// </param>
  61. private async void LoginAction(string provider)
  62. {
  63. Exception exception = null;
  64. bool isToShowMessage = false;
  65. try
  66. {
  67. InProgress = true;
  68. var auth = await _sessionService.LoginAsync(provider);
  69. if (!auth)
  70. {
  71. await _messageBox.ShowAsync(AppResources.LoginView_LoginNotAllowed_Message,
  72. AppResources.MessageBox_Title,
  73. new List<string>
  74. {
  75. AppResources.Button_OK
  76. });
  77. }
  78. else
  79. {
  80. _navigationService.NavigateTo(new Uri(Constants.MainView, UriKind.Relative));
  81. }
  82. InProgress = false;
  83. }
  84. catch (InvalidOperationException e)
  85. {
  86. InProgress = false;
  87. isToShowMessage = true;
  88. }
  89. catch (Exception ex)
  90. {
  91. exception = ex;
  92. }
  93. if (isToShowMessage)
  94. {
  95. await _messageBox.ShowAsync(AppResources.LoginView_AuthFail, AppResources.ApplicationTitle, new List<string> { AppResources.Button_OK });
  96. }
  97. if (exception != null)
  98. {
  99. await _logManager.LogAsync(exception);
  100. }
  101. }
  102. }

Note: in LoginAction the parameter provider is the value of the CommandParameter received in the LoginCommand, this is set in the login page.

The LoginPage.xaml is:

XAML

  1. <phone:PhoneApplicationPage x:Class="AuthenticationSample.WP80.Views.LoginView"
  2. xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  3. xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  4. xmlns:Command="clr-namespace:GalaSoft.MvvmLight.Command;assembly=GalaSoft.MvvmLight.Extras.WP8"
  5. xmlns:controls="clr-namespace:Facebook.Client.Controls;assembly=Facebook.Client"
  6. xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
  7. xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
  8. xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
  9. xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
  10. xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
  11. xmlns:converters="clr-namespace:Cimbalino.Phone.Toolkit.Converters;assembly=Cimbalino.Phone.Toolkit"
  12. Orientation="Portrait"
  13. SupportedOrientations="Portrait"
  14. shell:SystemTray.IsVisible="True"
  15. mc:Ignorable="d">
  16. <phone:PhoneApplicationPage.DataContext>
  17. <Binding Mode="OneWay"
  18. Path="LoginViewModel"
  19. Source="{StaticResource Locator}" />
  20. </phone:PhoneApplicationPage.DataContext>
  21. <phone:PhoneApplicationPage.Resources>
  22. <converters:BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
  23. </phone:PhoneApplicationPage.Resources>
  24. <phone:PhoneApplicationPage.FontFamily>
  25. <StaticResource ResourceKey="PhoneFontFamilyNormal" />
  26. </phone:PhoneApplicationPage.FontFamily>
  27. <phone:PhoneApplicationPage.FontSize>
  28. <StaticResource ResourceKey="PhoneFontSizeNormal" />
  29. </phone:PhoneApplicationPage.FontSize>
  30. <phone:PhoneApplicationPage.Foreground>
  31. <StaticResource ResourceKey="PhoneForegroundBrush" />
  32. </phone:PhoneApplicationPage.Foreground>
  33. <!-- LayoutRoot is the root grid where all page content is placed -->
  34. <Grid x:Name="LayoutRoot" Background="Transparent">
  35. <Grid.RowDefinitions>
  36. <RowDefinition Height="Auto" />
  37. <RowDefinition Height="*" />
  38. </Grid.RowDefinitions>
  39. <!-- TitlePanel contains the name of the application and page title -->
  40. <StackPanel x:Name="TitlePanel"
  41. Grid.Row="0"
  42. Margin="12,17,0,28">
  43. <TextBlock Margin="12,0"
  44. Style="{StaticResource PhoneTextNormalStyle}"
  45. Text="{Binding LocalizedResources.ApplicationTitle,
  46. Mode=OneWay,
  47. Source={StaticResource LocalizedStrings}}" />
  48. <TextBlock Margin="9,-7,0,0"
  49. Style="{StaticResource PhoneTextTitle1Style}"
  50. Text="{Binding LocalizedResources.LoginView_Title,
  51. Mode=OneWay,
  52. Source={StaticResource LocalizedStrings}}" />
  53. </StackPanel>
  54. <!-- ContentPanel - place additional content here -->
  55. <Grid x:Name="ContentPanel"
  56. Grid.Row="1"
  57. Margin="24,0,0,-40">
  58. <Grid.RowDefinitions>
  59. <RowDefinition Height="Auto" />
  60. <RowDefinition Height="Auto" />
  61. <RowDefinition Height="Auto" />
  62. <RowDefinition Height="Auto" />
  63. <RowDefinition Height="Auto" />
  64. </Grid.RowDefinitions>
  65. <TextBlock Grid.Row="0"
  66. Style="{StaticResource PhoneTextTitle2Style}"
  67. Text="{Binding LocalizedResources.LoginView_UserAccount,
  68. Mode=OneWay,
  69. Source={StaticResource LocalizedStrings}}" />
  70. <Button Grid.Row="1"
  71. Margin="10"
  72. Command="{Binding LoginCommand}"
  73. CommandParameter="facebook"
  74. Content="Facebook" />
  75. <Button Grid.Row="2"
  76. Margin="10"
  77. Command="{Binding LoginCommand}"
  78. CommandParameter="microsoft"
  79. Content="Microsoft" />
  80. <Button Grid.Row="3"
  81. Margin="10"
  82. Command="{Binding LoginCommand}"
  83. CommandParameter="google"
  84. Content="Google" />
  85. </Grid>
  86. <Grid Visibility="{Binding InProgress, Converter={StaticResource BooleanToVisibilityConverter}}"
  87. Grid.Row="0"
  88. Grid.RowSpan="2">
  89. <Rectangle
  90. Fill="Black"
  91. Opacity="0.75" />
  92. <TextBlock
  93. HorizontalAlignment="Center"
  94. VerticalAlignment="Center"
  95. Text="{Binding LocalizedResources.LoginView_AuthMessage,
  96. Mode=OneWay,
  97. Source={StaticResource LocalizedStrings}}" />
  98. <ProgressBar IsIndeterminate="True" IsEnabled="True" Margin="0,60,0,0"/>
  99. </Grid>
  100. </Grid>
  101. </phone:PhoneApplicationPage>

Login User Interface

Run

Source Code Files

Build the Sample

  1. Start Visual Studio Express 2012 for Windows 8 and select File > Open > Project/Solution.
  2. Go to the directory in which you unzipped the sample. Go to the directory named for the sample, and double-click the Visual Studio Express 2012 for Windows 8 Solution (.sln) file.
  3. Press F7 or use Build > Build Solution to build the sample.

Note: you can use Visual Studio 2013 in Windows 8.1.

Run the sample

To debug the app and then run it, press F5 or use Debug > Start Debugging. To run the app without debugging, press Ctrl+F5 or use Debug > Start Without Debugging.

Related Samples