两部分问题:在启动时,My Xamarin.Forms app.cs使用按钮导航到登录页面(ContentPage)。我单击按钮,然后使用后面的应用程序代码中的此事件处理程序成功登录:
async void OnLoginButtonClicked(object sender, EventArgs e)
{
try
{
bool authenticated = await App.AuthenticationProvider.LoginAsync();
if (authenticated)
{
Application.Current.MainPage = new PapMobLandingPage();
}
else
{
await DisplayAlert("Authentication", "Authentication", "OK");
}
}
catch (MsalException ex)
{
if (ex.ErrorCode == "authentication_canceled")
{
await DisplayAlert("Authentication", "Authentication was cancelled by the user.", "OK");
}
else
{
await DisplayAlert("An error has occurred", "Exception message: " + ex.Message, "OK");
}
}
catch (Exception ex)
{
await DisplayAlert("Authentication", "Authentication failed in a big way. Exception: " + ex.Message, "OK");
}
}
}
然后我被重定向到第2页(PapMobLandingPage),该页面也有一个按钮。我单击该PapMobLandingPage按钮,然后重定向到TabbedPage,其中包含从我的Azure SQL数据库飞来的登录用户的详细信息,没有问题。直到现在一切都好!这是事件处理程序:
public async void GoToTabbedPage(object sender, EventArgs args)
{
await Xamarin.Forms.Application.Current.MainPage.Navigation.PushModalAsync(new BotInTabbedPage());
}
问题1:BotInTabbedPage(TabbedPage)代码中没有OnAppearing(),该代码检查用户是否已登录... TabbedPage初始化中没有获取令牌,因此应用程序如何知道我已在此页面上登录?
我从@Creepin看了一个类似的问题,并且使用该链接我无法理解他最初的问题的答案,即您是否必须分别验证每个页面。链接到这里:
use of AcquireTokenSilentAsync
我问问题1的原因是上面的选项卡式页面是用户登录时选择的六个选项之一,因此我需要一个包含六个图块的仪表板页面(基于MVVM)。当我将其插入到Page 2(PapMobLandingPage)和BotInTabbedPage(TabbedPage)之间,然后单击TabbedPage磁贴时,链接到该磁贴的tap命令将我带到BotInTabbedPage(TabbedPage)...但是...
没有客户端数据!我已经登出!
所以总结一下:
登录页面-> PapMobLandingPage-> BotInTabbedPage =保持身份验证。 登录页面-> PapMobLandingPage->仪表板-> BotInTabbedPage =放弃身份验证。
如果我使用带有按钮的“原始” ContentPage:
登录页面-> PapMobLandingPage-> ContentPage-> BotInTabbedPage =保持身份验证!
第二个问题是:有人知道为什么吗?
当我说Dashboard基于MVVM时,我的意思是它具有XAML ContentPage,后面是.cs代码,具有对视图模型的值绑定,该视图模型也具有视图模型模板和模板库。这是代码:
仪表板XAML:
<?xml version="1.0" encoding="utf-8"?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:PapWine;assembly=PapWine"
xmlns:artina="clr-namespace:UXDivers.Artina.Shared;assembly=UXDivers.Artina.Shared"
x:Class="PapWine.DashboardTaskMultipleTilesPage"
BackgroundColor="Black"
Title="{ artina:Translate PageTitleDashboardTaskMultipleTiles }">
<ContentPage.Resources>
<ResourceDictionary>
<artina:BoolMemberTemplateSelector
x:Key="Selector"
MemberName="IsNotification">
<artina:BoolMemberTemplateSelector.TrueDataTemplate>
<DataTemplate>
<local:DashboardAppNotificationItemTemplate
WidthRequest="145"
HeightRequest="145" />
</DataTemplate>
</artina:BoolMemberTemplateSelector.TrueDataTemplate>
<artina:BoolMemberTemplateSelector.FalseDataTemplate>
<DataTemplate>
<local:TaskTilesItemTemplate
ShowBackgroundImage="true"
ShowBackgroundColor="true"
ShowiconColoredCircleBackground="false"
TextColor="{ DynamicResource DashboardIconColor }"
WidthRequest="145"
HeightRequest="145"
/>
</DataTemplate>
</artina:BoolMemberTemplateSelector.FalseDataTemplate>
</artina:BoolMemberTemplateSelector>
</ResourceDictionary>
</ContentPage.Resources>
<ScrollView
Orientation="Both">
<artina:GridOptionsView
WidthRequest="320"
Margin="0"
Padding="10"
ColumnSpacing="10"
RowSpacing="10"
ColumnCount="2"
ItemsSource="{Binding DashboardTaskMultipleTilesList}"
ItemTemplate="{StaticResource Selector}"
/>
</ScrollView>
</ContentPage>
仪表板XAML.cs
using Microsoft.Identity.Client;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Xamarin.Forms;
namespace PapWine
{
public partial class DashboardTaskMultipleTilesPage : ContentPage
{
public DashboardTaskMultipleTilesPage()
{
InitializeComponent();
NavigationPage.SetHasNavigationBar(this, true);
BindingContext = new DashboardTaskMultipleTilesViewModel();
}
protected override async void OnAppearing()
{
base.OnAppearing();
PublicClientApplication PCA = new PublicClientApplication(Constants.ClientID, Constants.Authority);
IEnumerable<IAccount> accounts = await PCA.GetAccountsAsync();
AuthenticationResult authenticationResult = await PCA.AcquireTokenSilentAsync(Constants.Scopes, GetAccountByPolicy(accounts, Constants.PolicySignUpSignIn), Constants.Authority, false);
JObject user = ParseIdToken(authenticationResult.IdToken);
var currentuseroid = user["oid"]?.ToString();
}
private IAccount GetAccountByPolicy(IEnumerable<IAccount> accounts, string policy)
{
foreach (var account in accounts)
{
string userIdentifier = account.HomeAccountId.ObjectId.Split('.')[0];
if (userIdentifier.EndsWith(policy.ToLower())) return account;
}
return null;
}
JObject ParseIdToken(string idToken)
{
// Get the piece with actual user info
idToken = idToken.Split('.')[1];
idToken = Base64UrlDecode(idToken);
return JObject.Parse(idToken);
}
string Base64UrlDecode(string str)
{
str = str.Replace('-', '+').Replace('_', '/');
str = str.PadRight(str.Length + (4 - str.Length % 4) % 4, '=');
var byteArray = Convert.FromBase64String(str);
var decoded = Encoding.UTF8.GetString(byteArray, 0, byteArray.Count());
return decoded;
}
}
}
仪表板视图模型:
using System.Collections.Generic;
using System.Globalization;
using System.Threading.Tasks;
using Xamarin.Forms;
namespace PapWine
{
public class DashboardTaskMultipleTilesViewModel : ObservableObject
{
private List<DashboardTaskMultipleTileItem> _dashboardTaskMultipleTilesList;
public DashboardTaskMultipleTilesViewModel()
: base(listenCultureChanges: true)
{
LoadData();
}
public List<DashboardTaskMultipleTileItem> DashboardTaskMultipleTilesList
{
get { return _dashboardTaskMultipleTilesList; }
set { SetProperty(ref _dashboardTaskMultipleTilesList, value); }
}
protected override void OnCultureChanged(CultureInfo culture)
{
LoadData();
}
public async Task LogMeOut()
{
bool loggedOut = await App.AuthenticationProvider.LogoutAsync();
if (loggedOut)
{
Application.Current.MainPage = new LandingPagePreLogin();
}
}
private void LoadData()
{
DashboardTaskMultipleTilesList = new List<DashboardTaskMultipleTileItem>
{
//1 line
new DashboardTaskMultipleTileItem
{
Title = "Log Out",
Body = "",
Avatar = "",
BackgroundColor = "transparent",
ShowBackgroundColor = false,
IsNotification = false,
BackgroundImage = "Tiles/DarkBlackTile.jpg",
Icon = FontAwesomeFont.Lock,
IconColour = "White"
},
new DashboardTaskMultipleTileItem
{
Title = "User Settings",
Body = "",
Avatar = "",
BackgroundColor = "transparent",
ShowBackgroundColor = false,
IsNotification = false,
BackgroundImage = "Tiles/DarkBlackTile.jpg",
Icon = FontAwesomeFont.Gear,
IconColour = "White"
},
//2 line
new DashboardTaskMultipleTileItem
{
Title = "User Info",
Body = "",
Avatar = "",
BackgroundColor = "transparent",
ShowBackgroundColor = false,
IsNotification = false,
BackgroundImage = "Tiles/DarkBlackTile.jpg",
Icon = FontAwesomeFont.User,
Badge = 12,
IconColour = "White"
},
new DashboardTaskMultipleTileItem
{
Title = "Papillon Shop",
Body = "",
Avatar = "",
BackgroundColor = "transparent",
ShowBackgroundColor = false,
IsNotification = false,
BackgroundImage = "Tiles/DarkBlackTile.jpg",
Icon = FontAwesomeWeb511Font.store,
Badge = 2,
IconColour = "White"
},
//3 line
new DashboardTaskMultipleTileItem
{
Title = "Check Bottles In",
Body = "",
Avatar = "",
BackgroundColor = "transparent",
ShowBackgroundColor = false,
IsNotification = false,
BackgroundImage = "Tiles/DarkBlackTile.jpg",
Icon = FontAwesomeFont.Book,
IconColour = "White"
},
new DashboardTaskMultipleTileItem
{
Title = "Lay Bottles Down",
Body = "",
Avatar = "",
BackgroundColor = "transparent",
ShowBackgroundColor = false,
IsNotification = false,
BackgroundImage = "Tiles/DarkBlackTile.jpg",
Icon = FontAwesomeFont.Bed,
Badge = 2,
IconColour = "White"
},
};
}
}
public class DashboardTaskMultipleTileItem
{
public string Title { get; set; }
public string Body { get; set; }
public string Avatar { get; set; }
public string BackgroundColor { get; set; }
public string BackgroundImage { get; set; }
public bool ShowBackgroundColor { get; set; }
public bool IsNotification { get; set; }
public string Icon { get; set; }
public int Badge { get; set; }
public string NavigPage { get; set; }
private Xamarin.Forms.Command _tapCommand;
public Xamarin.Forms.Command TapCommand
{
get
{
if (_tapCommand == null)
{
switch (this.Title) {
case "Log Out":
App.AuthenticationProvider.LogoutAsync();
_tapCommand = new Xamarin.Forms.Command(() =>
Xamarin.Forms.Application.Current.MainPage.Navigation.PushModalAsync(new LogoutSuccessPage()));
break;
case "User Settings":
_tapCommand = new Xamarin.Forms.Command(() =>
Xamarin.Forms.Application.Current.MainPage.Navigation.PushAsync(new UserSettingsPage()));
break;
case "User Info":
_tapCommand = new Xamarin.Forms.Command(() =>
Xamarin.Forms.Application.Current.MainPage.Navigation.PushAsync(new UserProfilePage()));
break;
case "Papillon Shop":
_tapCommand = new Xamarin.Forms.Command(() =>
Xamarin.Forms.Application.Current.MainPage.Navigation.PushAsync(new ProductFamilyMultipleTilesPage()));
break;
case "Check Bottles In":
_tapCommand = new Xamarin.Forms.Command(() =>
Xamarin.Forms.Application.Current.MainPage = new SignBottlesIn());
break;
case "Check Bottles Out":
_tapCommand = new Xamarin.Forms.Command(() =>
Xamarin.Forms.Application.Current.MainPage = new SignBottlesIn());
break;
}
}
return _tapCommand;
}
}
}
}
标题项模板:
using Xamarin.Forms;
namespace PapWine
{
public partial class TaskTilesItemTemplate : TaskTilesItemTemplateBase
{
public TaskTilesItemTemplate()
{
InitializeComponent();
}
private async void OnTileTapped(object sender, ItemTappedEventArgs e)
{
if (e.Item == null)
return;
var content = e.Item as DashboardTaskMultipleTileItem;
//await Navigation.PushAsync(new LayBottlesDown()); //pass content if you want to pass the clicked item object to another page
}
}
}
标题项目模板库:
using System;
using System.Threading.Tasks;
using Xamarin.Forms;
namespace PapWine
{
public class TaskTilesItemTemplateBase : ContentView
{
public uint animationDuration = 250;
public bool _processingTag = false;
public static readonly BindableProperty ShowBackgroundImageProperty =
BindableProperty.Create(
nameof(ShowBackgroundImage),
typeof(bool),
typeof(TaskTilesItemTemplate),
true,
defaultBindingMode: BindingMode.OneWay
);
public bool ShowBackgroundImage
{
get { return (bool)GetValue(ShowBackgroundImageProperty); }
set { SetValue(ShowBackgroundImageProperty, value); }
}
public static readonly BindableProperty ShowBackgroundColorProperty =
BindableProperty.Create (
nameof( ShowBackgroundColor ),
typeof ( bool ),
typeof ( TaskTilesItemTemplate ),
false,
defaultBindingMode : BindingMode.OneWay
);
public bool ShowBackgroundColor {
get { return ( bool )GetValue( ShowBackgroundColorProperty ); }
set { SetValue ( ShowBackgroundColorProperty, value ); }
}
public static readonly BindableProperty ShowiconColoredCircleBackgroundProperty =
BindableProperty.Create (
nameof( ShowiconColoredCircleBackground ),
typeof ( bool ),
typeof (TaskTilesItemTemplate),
true,
defaultBindingMode : BindingMode.OneWay
);
public bool ShowiconColoredCircleBackground {
get { return ( bool )GetValue( ShowiconColoredCircleBackgroundProperty ); }
set { SetValue ( ShowiconColoredCircleBackgroundProperty, value ); }
}
public static readonly BindableProperty TextColorProperty =
BindableProperty.Create (
nameof( TextColor ),
typeof ( Color ),
typeof (TaskTilesItemTemplate),
defaultValue : Color.White,
defaultBindingMode : BindingMode.OneWay
);
public Color TextColor {
get { return ( Color )GetValue( TextColorProperty ); }
set { SetValue ( TextColorProperty, value ); }
}
//added start
public Color IconColour
{
get { return (Color)GetValue(TextColorProperty); }
set { SetValue(TextColorProperty, value); }
}
//added end
public async void OnWidgetTapped(object sender, EventArgs e)
{
if (_processingTag)
{
return;
}
_processingTag = true;
try{
await AnimateItem (this, animationDuration );
await SamplesListFromCategoryPage.NavigateToCategory ((SampleCategory)BindingContext, Navigation);
}finally{
_processingTag = false;
}
}
private async Task AnimateItem(View uiElement, uint duration ){
var originalOpacity = uiElement.Opacity;
await uiElement.FadeTo(.5, duration/2, Easing.CubicIn);
await uiElement.FadeTo(originalOpacity, duration/2, Easing.CubicIn);
}
}
}
LoginAsync看起来像这样:
public async Task<bool> LoginAsync(bool useSilent = false)
{
bool success = false;
//AuthenticationResult authResult = null;
try
{
AuthenticationResult authenticationResult;
if (useSilent)
{
authenticationResult = await ADB2CClient.AcquireTokenSilentAsync(
Constants.Scopes,
GetAccountByPolicy(await ADB2CClient.GetAccountsAsync(), Constants.PolicySignUpSignIn),
Constants.Authority,
false);
UpdateUserInfo(authenticationResult);
}
else
{
authenticationResult = await ADB2CClient.AcquireTokenAsync(
Constants.Scopes,
GetAccountByPolicy(await ADB2CClient.GetAccountsAsync(), Constants.PolicySignUpSignIn),
App.UiParent);
}
if (User == null)
{
var payload = new JObject();
if (authenticationResult != null && !string.IsNullOrWhiteSpace(authenticationResult.IdToken))
{
payload["access_token"] = authenticationResult.IdToken;
}
User = await TodoItemManager.DefaultManager.CurrentClient.LoginAsync(
MobileServiceAuthenticationProvider.WindowsAzureActiveDirectory,
payload);
success = true;
}
}