Can't open modal on new main page - xamarin

This code fully demonstrates what looks like a bug. Sequence is...
App opens
Default main page created.
Login box popped up.
Login box closed. (Click on page to simulate)
New main page created.
Login box popped up.(Click on page to simulate)
Exception... Cannot access disposed object.
Any ideas?
using System;
using System.Threading.Tasks;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
[assembly: XamlCompilation(XamlCompilationOptions.Compile)]
namespace test333
{
public class Main1 : ContentPage
{
public Main1()
{
var btn = new Button();
btn.Clicked += (sender, e) =>
{
MessagingCenter.Send<object>(this, "LoginLogout");
Navigation.PopModalAsync();
};
Content = btn;
}
}
public partial class App : Application
{
public App()
{
InitializeComponent();
MainPage = new Main1 { BackgroundColor = Color.Red };
MessagingCenter.Subscribe<object>(this, "LoginLogout", s =>
Device.BeginInvokeOnMainThread(async () => await HandleLogOut()));
HandleLogOut();
}
bool login = true;
async Task HandleLogOut()
{
if (login)
{
await MainPage.Navigation.PushModalAsync(new Main1 { BackgroundColor = Color.Orange });
}
else
{
MainPage = new Main1 { BackgroundColor = Color.Green };
}
login = !login;
}
}
}

I think the problem happens in step 6:Login box popped up.(Click on page to simulate).
Have a look at below codes in btn.clicked:
btn.Clicked += (sender, e) =>
{
MessagingCenter.Send<object>(this, "LoginLogout");
Console.WriteLine("popAction");
Navigation.PopModalAsync();
};
And handle logout:
async Task HandleLogOut()
{
if (login)
{
await MainPage.Navigation.PushModalAsync(new Main1 { BackgroundColor = Color.Orange });
Console.WriteLine("PushModalAction");
}
else
{
MainPage = new Main1 { BackgroundColor = Color.Green };
}
login = !login;
}
I print the popAction and PushAction in your code, you will find that the popAction is executed before PushAction.
Solution:
You should perform popAction after pushModalAction is completed.
I moved the Navigation.PopModalAsync(); from the btn.clicked to HandleLogOut and then it works well.
btn.Clicked += async (sender, e) =>
{
MessagingCenter.Send<object>(this, "LoginLogout");
};
In handle logout:
async Task HandleLogOut()
{
if (login)
{
Console.WriteLine("PushModalAction");
await MainPage.Navigation.PushModalAsync(new Main1 { BackgroundColor = Color.Orange });
Console.WriteLine("popAction");
MainPage.Navigation.PopModalAsync();
}
else
{
MainPage = new Main1 { BackgroundColor = Color.Green };
}
login = !login;
}

Related

DisplayAlert Xamarim forms

I Have this:
var accept = await DisplayAlert("Title", "ask", "yes", "not");
I would like this displayalert to be visible for 5 seconds if the user does not choose any option the displayalert disappears
how can i do this?
Welcome to SO !
Unfortunately , Xamarin Forms not provides some like Dismiss method for alert windown . However , we can use other ways to achieve that . Such as using Xamarin.Forms DependencyService .
First , we can create a IShowAlertService interface in xamarin forms :
public interface IShowAlertService
{
Task<bool> ShowAlert(string title,string message,string ok, string cancel);
}
Next in iOS , Create its implement class :
[assembly: Dependency(typeof(ShowAlertService))]
namespace XamarinTableView.iOS
{
class ShowAlertService : IShowAlertService
{
TaskCompletionSource<bool> taskCompletionSource;
public Task<bool> ShowAlert(string title, string message, string ok, string cancel)
{
taskCompletionSource = new TaskCompletionSource<bool>();
var okCancelAlertController = UIAlertController.Create(title, message, UIAlertControllerStyle.Alert);
//Add Actions
okCancelAlertController.AddAction(UIAlertAction.Create(ok, UIAlertActionStyle.Default, alert => {
Console.WriteLine("Okay was clicked");
taskCompletionSource.SetResult(true);
}));
okCancelAlertController.AddAction(UIAlertAction.Create(cancel, UIAlertActionStyle.Cancel, alert => {
Console.WriteLine("Cancel was clicked");
taskCompletionSource.SetResult(true);
}));
UIWindow window = UIApplication.SharedApplication.KeyWindow;
var viewController = window.RootViewController;
//Present Alert
viewController.PresentViewController(okCancelAlertController, true, null);
Device.StartTimer(new TimeSpan(0, 0, 3), () =>
{
taskCompletionSource.SetResult(false);
Device.BeginInvokeOnMainThread(() =>
{
okCancelAlertController.DismissViewController(true,null);
// interact with UI elements
Console.WriteLine("Auto Dismiss");
});
return false; // runs again, or false to stop
});
return taskCompletionSource.Task;
}
}
And in Android , also do that :
[assembly: Dependency(typeof(ShowAlertService))]
namespace XamarinTableView.Droid
{
class ShowAlertService : IShowAlertService
{
TaskCompletionSource<bool> taskCompletionSource;
public Task<bool> ShowAlert(string title, string message, string ok, string cancel)
{
taskCompletionSource = new TaskCompletionSource<bool>();
Android.App.AlertDialog.Builder dialog = new AlertDialog.Builder(MainActivity.Instance);
AlertDialog alert = dialog.Create();
alert.SetTitle("Title");
alert.SetMessage("Complex Alert");
alert.SetButton("OK", (c, ev) =>
{
// Ok button click task
Console.WriteLine("Okay was clicked");
taskCompletionSource.SetResult(true);
});
alert.SetButton2("CANCEL", (c, ev) => {
Console.WriteLine("Cancel was clicked");
taskCompletionSource.SetResult(false);
});
alert.Show();
Device.StartTimer(new TimeSpan(0, 0, 3), () =>
{
if (taskCompletionSource.Equals(null))
taskCompletionSource.SetResult(false);
Device.BeginInvokeOnMainThread(() =>
{
alert.Dismiss();
// interact with UI elements
Console.WriteLine("Auto Dismiss");
});
return false; // runs again, or false to stop
});
return taskCompletionSource.Task;
}
}
}
Here in Android , need to create a static Instance in MainActivity . Becasue we need to use it in ShowAlertService :
public class MainActivity : FormsAppCompatActivity
{
internal static MainActivity Instance { get; private set; }
protected override void OnCreate(Bundle savedInstanceState)
{
// ...
Instance = this;
}
//...
}
Now in Xamarin Forms , we can involke it as follow :
private async void Button_Clicked(object sender, EventArgs e)
{
bool value = await DependencyService.Get<IShowAlertService>().ShowAlert("Alert", "You have been alerted", "OK", "Cancel");
Console.WriteLine("Value is : "+value);
}
The effect as follow :

while deny the camera permission in android it wont ask again

first time application asking permission for the camera, if we deny the permission it won't ask again if we allow its working fine ??
var scanPage = new ZXingScannerPage();
var cameraStatus = await CrossPermissions.Current.CheckPermissionStatusAsync(Permission.Camera);
if (cameraStatus != PermissionStatus.Granted)
{
var results = await CrossPermissions.Current.RequestPermissionsAsync(new[] { Permission.Camera });
cameraStatus = results[Permission.Camera];
if (cameraStatus == PermissionStatus.Granted)
{
// Navigate to our scanner page
await Navigation.PushAsync(scanPage);
scanPage.OnScanResult += (result) =>
{
Device.BeginInvokeOnMainThread(async () =>
{
await Navigation.PopAsync();
txtbarcode.Text = result.Text;
});
};
}
else if (cameraStatus == PermissionStatus.Unknown)
{
await Navigation.PushAsync(scanPage);
scanPage.OnScanResult += (result) =>
{
Device.BeginInvokeOnMainThread(async () =>
{
await Navigation.PopAsync();
txtbarcode.Text = result.Text;
});
};
}
If we deny the camera permission, again its asks while opening camera until we allow the permission.
I wrote a demo about this.
https://github.com/851265601/CheckPermissionDemo
This a GIF of this demo.
Did you check the permission every time when you use the camera function? In this demo, when i click the button ,it will check the permission, then give the result to users, if not give the permission, it will still ask the persmission like the following code.
async void ButtonPermission_OnClicked(object sender, EventArgs e)
{
if (busy)
return;
busy = true;
((Button)sender).IsEnabled = false;
var status = await CrossPermissions.Current.CheckPermissionStatusAsync(Permission.Camera);
await DisplayAlert("Pre - Results", status.ToString(), "OK");
if (status != PermissionStatus.Granted)
{
status = await Utils.CheckPermissions(Permission.Camera);
await DisplayAlert("Results", status.ToString(), "OK");
}
busy = false;
((Button)sender).IsEnabled = true;
}
}
When DisplayAlert was appear, it are used MainApplication for different life cycle
namespace CheckPermissionDemo.Droid
{
//You can specify additional application information in this attribute
[Application]
public class MainApplication : Application, Application.IActivityLifecycleCallbacks
{
public MainApplication(IntPtr handle, JniHandleOwnership transer)
: base(handle, transer)
{
}
public override void OnCreate()
{
base.OnCreate();
RegisterActivityLifecycleCallbacks(this);
//A great place to initialize Xamarin.Insights and Dependency Services!
}
public override void OnTerminate()
{
base.OnTerminate();
UnregisterActivityLifecycleCallbacks(this);
}
public void OnActivityCreated(Activity activity, Bundle savedInstanceState)
{
CrossCurrentActivity.Current.Activity = activity;
}
public void OnActivityDestroyed(Activity activity)
{
}
public void OnActivityPaused(Activity activity)
{
}
public void OnActivityResumed(Activity activity)
{
CrossCurrentActivity.Current.Activity = activity;
}
public void OnActivitySaveInstanceState(Activity activity, Bundle outState)
{
}
public void OnActivityStarted(Activity activity)
{
CrossCurrentActivity.Current.Activity = activity;
}
public void OnActivityStopped(Activity activity)
{
}
}
}

Azure AD B2C Authentication dropped when navigating to an MVVM-based page

Two part question: On starting up, My Xamarin.Forms app.cs navigates to a login page (ContentPage) with a button. I click the button and I login successfully with this event handler in the app code behind:
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");
}
}
}
I then get redirected to Page 2 (PapMobLandingPage) which also has a button. I click that PapMobLandingPage button and get redirected to a TabbedPage with the details of the logged in user flying right in there from my Azure SQL Database, no problems. All great til now! Here is the event handler:
public async void GoToTabbedPage(object sender, EventArgs args)
{
await Xamarin.Forms.Application.Current.MainPage.Navigation.PushModalAsync(new BotInTabbedPage());
}
Question 1: There is no OnAppearing() in the BotInTabbedPage (TabbedPage) code behind that checks if the user is logged in... there is no acquiring of tokens going on in the TabbedPage initialization, so how does the app know that I'm logged in on this page??
I looked at a similar question from #Creepin and using the link I could not understand the answer to his original question as to whether you have to authenticate each page individually. Link here:
use of AcquireTokenSilentAsync
The reason I ask question 1 is that the tabbed page above is one of six choices the user gets on sign in, so I needed a dashboard page (MVVM based) with six tiles. When I insert this between Page 2 (PapMobLandingPage) and BotInTabbedPage (TabbedPage), and click on the TabbedPage tile, the tapcommand linked to the tile takes me to the BotInTabbedPage (TabbedPage)... BUT...
NO CLIENT DATA! I HAVE BEEN LOGGED OUT!
So to sum up:
Login Page -> PapMobLandingPage -> BotInTabbedPage = Stays authenticated.
Login Page -> PapMobLandingPage -> Dashboard -> BotInTabbedPage = Drops authentication.
If I use a "vanilla" ContentPage with a button:
Login Page -> PapMobLandingPage -> ContentPage -> BotInTabbedPage = Stays authenticated!
So second question is: does anyone have a clue why?
When I say the Dashboard is MVVM based I mean it has a XAML ContentPage, a .cs code behind, with value bindings to a view model which also has a view model template and a template base. This is the code:
Dashboard 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>
Dashboard 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;
}
}
}
Dashboard View Model:
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;
}
}
}
}
Tiles Item Template:
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
}
}
}
Tiles Item Template Base:
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 looks like this:
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;
}
}

Xamarin Forms Grid - change cell view via button

I have a page in which I display two different charts, depending on a button click. At the moment I change charts this way:
protected void ClickedChangeChart()
{
if (chartType == true)
{
chartType = false;
Navigation.PushAsync (new MainPage (false));
}
else
{
chartType = true;
Navigation.PushAsync (new MainPage (true));
}
}
chartType is a bool and depending on it's value I choose which chart to load using this statement in OnAppearing():
protected override async void OnAppearing ()
{
// some code
ChartView chartView = new ChartView
{
VerticalOptions = LayoutOptions.FillAndExpand,
HorizontalOptions = LayoutOptions.FillAndExpand,
HeightRequest = 300,
WidthRequest = 400
};
if (chartType == true)//true = candle
{
candleModel = new CandleModel ();
chartView.Model = await candleModel.GetModel ();
}
else if(chartType == false) //false = line
{
lineModel = new LineModel ();
chartView.Model = await lineModel.GetModel ();
}
//here I create a grid, add some children to it and the add the chartView
grid.Children.Add (chartView, 1, 6, 1, 3);
Content = grid;
}
The problem is that when I want to switch the charts I have to reload the whole page, which isn't what I want. How can I make it so when I click a button I calls a function which switches the model for the chart? I suppose it will be something like this, but I can't get it to work:
public async void ClickedButton()
{
grid.Children.Remove(chartView);
if (chartType == true)
{
candleModel = new CandleModel ();
chartView.Model = await candleModel.GetModel ();
}
else if(chartType == false)
{
lineModel = new LineModel ();
chartView.Model = await lineModel.GetModel ();
}
grid.Children.Add(chartView);
}
UPDATE:
Using Daniel Luberda's solution, I managed to get it to work with this:
btnChartType.Clicked += async delegate {
Device.BeginInvokeOnMainThread (async () => {
grid.Children.Remove (chartView);
if (isCandle == true) {
candleModel = new CandleModel ();
chartView.Model = await candleModel.GetModel ();
isCandle = false;
} else if (isCandle == false) {
lineModel = new LineModel ();
chartView.Model = await lineModel.GetModel ();
isCandle = true;
}
grid.Children.Add (chartView, 1, 6, 1, 3);
});
};
You can't get it to work because you have an async method which means that you're doing it on another thread. You can change UI only from UI thread. Also async void will swallow all exceptions, it's better to use async Task instead.
// THAT WILL WORK
public async void ClickedButton() // but public async Task would be better
{
Device.BeginInvokeOnMainThread(async () => {
grid.Children.Remove(chartView);
if (chartType == true)
{
candleModel = new CandleModel ();
chartView.Model = await candleModel.GetModel ();
}
else if(chartType == false)
{
lineModel = new LineModel ();
chartView.Model = await lineModel.GetModel ();
}
grid.Children.Add(chartView);
});
}

Set Image Source on return from Gallery/Camera?

I have an Image view I'm having trouble setting the source for. I'm using a button to execute a TakePictureCommand which calls the TakePicture() method (shown below) which in turn sets my source "ImageSource". Debugging the method shows the image is coming in, but I never see it come up in the UI.
I may not be setting the binding for the Image properly, this is what I have:
Image avatar = new Image();
avatar.Source = ImageSource;
Button setImageBtn = new Button{ Text = "Photo" };
setImageBtn.Clicked += async (sender, e) =>
{
string action = await DisplayActionSheet(
"Event Photo", "Cancel", null, OPTION_CAMERA, OPTION_GALLERY);
if(action == OPTION_CAMERA) {
TakePictureCommand.Execute(null);
}
else if(action == OPTION_GALLERY) {
SelectPictureCommand.Execute(null);
}
};
TakePicture()
private async Task<MediaFile> TakePicture()
{
Setup();
ImageSource = null;
return await _mediaPicker.TakePhotoAsync(
new CameraMediaStorageOptions {
DefaultCamera = CameraDevice.Front,
MaxPixelDimension = 400
}).ContinueWith(t =>
{
if (t.IsFaulted)
{
Status = t.Exception.InnerException.ToString();
}
else if (t.IsCanceled)
{
Status = "Canceled";
}
else
{
var mediaFile = t.Result;
ImageSource = ImageSource.FromStream(() => mediaFile.Source);
return mediaFile;
}
return null;
}, _scheduler);
}
What am I missing here?
Below approach is working for me:
First, in XAML I added Image view
<Image Source="{Binding ImageSource}" VerticalOptions="Fill" HorizontalOptions="Fill"
Aspect="AspectFit"/>
Second, in ViewModel I added ImageSource property like this:
public ImageSource ImageSource
{
get { return _imageSource; }
set { this.SetProperty(ref _imageSource, value); }
}
Third, in command handler:
await TakePicture ();
Forth, code of TakePicture() method the same as you wrote.
My Setup():
if (_mediaPicker != null)
return;
var device = Resolver.Resolve<IDevice>();
_mediaPicker = DependencyService.Get<IMediaPicker>() ?? device.MediaPicker;

Resources