Xamarin / Xamarin.Forms - Deadlock when initializing MasterDetailPage? - c#

I have an Xamarin.Forms App. In the first step the user of the app has to login on a login page which is a simple ContentPage. After the user succesfully logged in he should see a MasterDetailPage.
My Problem now is that this code line produces a deadlock. So this code line will never finish.
_masterPage.Master = _mainMenuPage;
Here is the whole function:
private void SignInButtonClicked(string username, string password)
{
SignInAsync(username, password).ContinueWith(task =>
{
if (task.Result)
{
_signInPage.StopActivityIndicator();
_masterPage.Master = _mainMenuPage;
_masterPage.Detail = _masterNavigationPage;
MainPage = _masterPage;
}
}, System.Threading.CancellationToken.None, System.Threading.Tasks.TaskContinuationOptions.OnlyOnRanToCompletion, System.Threading.Tasks.TaskScheduler.FromCurrentSynchronizationContext());
}
SignInAsync:
private async System.Threading.Tasks.Task<bool> SignInAsync(string username, string password)
{
bool signedIn = true;
//ToDo SignIn logic
if (signedIn)
{
_mainMenuPage = new CPM.Arda.Mobile.Freelancer.Ui.Pages.MainMenu(this);
await _mainMenuPage.InitAsync();
await _mainMenuPage.RefreshDataAsync();
_mainMenuPage.MainMenuItemSelectedEvent += MainMenuItemSelected;
CPM.Arda.Mobile.Freelancer.Ui.Pages.General.Overview overviewPage = new CPM.Arda.Mobile.Freelancer.Ui.Pages.General.Overview(this);
await overviewPage.InitAsync();
await overviewPage.RefreshDataAsync();
_masterNavigationPage = new Xamarin.Forms.NavigationPage(overviewPage);
_masterPage = new CPM.Arda.Mobile.Freelancer.Ui.Pages.Master(this);
await _masterPage.InitAsync();
await _masterPage.RefreshDataAsync();
}
return signedIn;
}
Any ideas? It seems very strange to me.

I found the solution. I forgot to set the Title property on CPM.Arda.Mobile.Freelancer.Ui.Pages.MainMenu (the MasterPage from the MasterDetailPage). A common mistake with Xamarin.Forms.
But with no exception its very hard to find. Xamarin is not helpful sometimes :/

Related

Xamarin.Android MVVM removes View after opening BarcodeScanner or Camera

I have currently a problem with my C#-Xamarin.Android project.
We have to implement a barcode scanner plus functionality to take photos.
For the barcode scanner we use "Zxing.Net" and for the pictures "Xam.Media.Plugin".
Everything is working alright. Just until I am in their views don't finish the job and minimize the app. After maximizing it again, you can not navigate back - it just minimizes the app again. It seems that the previous views (MvxFragments) are getting destroyed.
I tried to move the picture functionality in a separate MvxViewModel but it is even worse because I have to return a string
public string FileName {get; set; } to the previous view. With await _navigationService.Close(this, FileName) (this = CameraViewModel) it results in a NullReferenceException coming from the previous ViewModel.
(1)
Navigation from (any) ViewModel1 to CameraViewModel:
var fileName = await _navigationService.Navigate<CameraViewModel, MyObject, string>(myObject);
Taking picture
public override async Task Initialize()
{
try
{
FileName = await TakePicture();
await _navigationService.Close(this, FileName);
}
catch (Exception e)
{
// handle exception
}
}
public async Task<string> TakePicture()
{
await CrossMedia.Current.Initialize();
if (!CrossMedia.Current.IsCameraAvailable || !CrossMedia.Current.IsTakePhotoSupported)
{
UserDialogs.Instance.Alert(Lang.NoCamera, Lang.Error, Lang.Ok);
return null;
}
string fileName = $"{}" // being generated here from different params
using var file = await CrossMedia.Current.TakePhotoAsync(cameraOptions);
if (file != null && file.GetStream().Length > 0)
{
using var ms = new MemoryStream();
await file.GetStream().CopyToAsync(ms);
file.Dispose();
// now save picture
return fileName;
}
return null;
}
Now after I navigate from ViewModel1 to CameraViewModel it opens the camera. After minimizing and maximizing again the app I can't navigate back -> it crashes.
If I move the code back to ViewModel1 and redoing all those steps it's just minimizing the application like there's no previous view to navigate back to. Taking a picture and hoping that it functions again doesn't work either. It gets minimized again.
(2)
Starting the barcode scanner from (any) ViewModel
public async Task DoScan()
{
var result = await _barcodeService.Read();
await Task.Delay(new TimeSpan(0, 0, 0, 1));
if (result.Success)
{
UserDialogs.Instance.Toast(result.Barcode);
BarcodeChanged(this, new BarcodeChangedEventArgs { Barcode = result.Barcode });
}
}
Barcode-Read
public async Task<BarcodeResult> Read(BarcodeReadConfiguration config, CancellationToken token)
{
config ??= BarcodeReadConfiguration.Default;
var scanner = new MobileBarcodeScanner
{
UseCustomOverlay = false
};
token.Register(scanner.Cancel);
var scanTask = scanner.Scan(GetXingConfig(config));
await Task.Delay(500, token);
scanner.Torch(true);
var result = await scanTask;
scanner.Torch(false);
return string.IsNullOrWhiteSpace(result?.Text) ? BarcodeResult.Fail : new BarcodeResult(result.Text, result.BarcodeFormat);
}
Same scenario as 1.. while the scanner is open and I minimize + maximize the app and then back press it just minimizes the app again. Even after reading a barcode.
Anyone knows why and how to fix it? Would help a lot..!
Edit: Here is a demo you can work with: https://github.com/softforgery/StackOverflow_Problem

Microsoft Graph throws Request_ResourceNotFound instead of null/0

I'm an apprentice with 4 months of experience and I got a task to build a holiday request application using data from Microsoft Graph. One of the functions of app is to look up a user'ss manager and display it on the dashboard. Everything was going smooth until my boss logged in. After running Microsoft Graph Query To find out current user Manager, Graph Api returns and error(Request_ResourceNotFound) and breaks whole application instead of returning null or 0. I don't know how to handle that error.
I have tried to return null if the result is null, but that didn't do anything.
This what my controller expects:
var allUsersConnectedToCurrentManagerDisplayName = graphHelper.GetManagerForCurrentUser(userIdToCheck).DisplayName;
var allUsersConnectedToCurrentManagerEmail = graphHelper.GetManagerForCurrentUser(userIdToCheck).UserPrincipalName;
var allUsersConnectedToCurrentManagerId = graphHelper.GetManagerForCurrentUser(userIdToCheck).Id;
Microsoft Graph Helper:
User GetDirectManagerForUser(GraphServiceClient _graphServiceClient, string managerId)
{
using(var task = Task.Run(async() => await _graphServiceClient.Users[managerId].Manager.Request().GetAsync()))
{
while (!task.IsCompleted)
Thread.Sleep(200);
var manager = task.Result as Microsoft.Graph.User;
return manager;
}
}
I was expecting this to return null and just don't display a direct manager for the user without anyone above him.
So you've got a few things going on here.
The first, and the most glaring, issue is that your code is requesting the same User record from Graph three times in a row. Each call you're making to GetDirectManagerForUser is downloading the entire User profile. You want to avoid doing this:
var manager = await graphHelper.GetManagerForCurrentUser(userIdToCheck);
var allUsersConnectedToCurrentManagerDisplayName = manager.DisplayName;
var allUsersConnectedToCurrentManagerEmail = manager.UserPrincipalName;
var allUsersConnectedToCurrentManagerId = manager.Id;
The second issue to avoid is wrapping your request in a Task like that. It adds a lot of complexity to the code, makes it super hard to debug, and isn't necessary. Simply add async Task<> at the method level and let the compiler handle wiring it up for you:
async Task<User> GetDirectManagerForUser(GraphServiceClient _graphServiceClient, string managerId)
Third, your casting the result but not capturing any exceptions (i.e. the 404 your getting). You want to capture these and return an empty User:
var manager = await graphHelper.GetManagerForCurrentUser(userIdToCheck);
var allUsersConnectedToCurrentManagerDisplayName = manager.DisplayName;
var allUsersConnectedToCurrentManagerEmail = manager.UserPrincipalName;
var allUsersConnectedToCurrentManagerId = manager.Id;
async Task<User> GetDirectManagerForUser(GraphServiceClient _graphServiceClient, string managerId)
{
try
{
// Normal path
return await _graphServiceClient
.Users[managerId]
.Manager
.Request()
.GetAsync();
}
catch (Exception)
{
// Something went wrong or no manager exists
var emptyUser = new User();
}
}
You have to catch the exception in order to return null.
I would write the function like this:
public User GetDirectManagerForUser(GraphServiceClient _graphServiceClient, string managerId)
{
//.Result, because this function in synchronious
try
{
var manager = await _graphServiceClient.Users[managerId].Manager.Request().GetAsync().Result;
return manager;
}
catch(Exception)
{
return null;
}
}
You could also make the function async like this:
public async Task<User> GetDirectManagerForUser(GraphServiceClient _graphServiceClient, string managerId)
{
try
{
var manager = await _graphServiceClient.Users[managerId].Manager.Request().GetAsync();
return manager;
}
catch(Exception)
{
return null;
}
}
Why haven't you specified an accessibility level?

Xamarin form :How to search in listview

I have listview containing data from web API. I want to search in the listview with character wise. The problem I am facing is when I start searching, it works fine but it gets very very slow. I need some solution to fix it. Here is my code:
private async void Entry_TextChanged(object sender, TextChangedEventArgs e)
{
var httpClient = new HttpClient();
var json = await httpClient.GetStringAsync(" http://172.16.4.212:51583/api/GetItems");
var admtPatients = JsonConvert.DeserializeObject<List<tblItem>>(json);
ObservableCollection<tblItem> trends = new ObservableCollection<tblItem>(admtPatients);
if (string.IsNullOrEmpty(medicine.Text))
{
MyListView.ItemsSource = trends;
}
else
{
MyListView.ItemsSource = trends
.Where(x =>
x.strItemName.ToLowerInvariant().Contains(e.NewTextValue.ToLowerInvariant()) ||
x.strItemName.ToUpperInvariant().Contains(e.NewTextValue.ToUpperInvariant()));
}
//await ((MainViewModel)this.BindingContext).LoadCountNotificationAsync();
}
Each time Entry_TextChanged is triggered, the call to GetStringAsync is done, which is very time consuming. This means that whenever the user presses a key a call to the API is made. This is why it is so slow.
You are better off calling GetStringAsync in the page's OnAppearing (for example), and saving the result globally:
private List<tblItem> listOfTableItems = new List<tblItem>();
protected override void OnAppearing()
{
var json = await httpClient.GetStringAsync("http://172.16.4.212:51583/api/GetItems");
listOfTableItems = JsonConvert.DeserializeObject<List<tblItem>>(json);
}
Then, in your Entry_TextChanged you reference listOfTableItems from the examples above:
if (String.IsNullOrEmpty(e.NewTextValue))
{
MyListView.ItemsSource = new ObservableCollection<tblItem>(listOfTableItems);
}
else
{
MyListView.ItemsSource = new ObservableCollection<tblItem>(listOfTableItems
.Where(x => x.strItemName.ToLowerInvariant().Contains(e.NewTextValue.ToLowerInvariant())));
}

Verify successful post in Facebook .NET sdk

I am creating a Windows Phone 8.1 RT application. I have installed the facebook and facebook.client sdks for setting up login and sharing purposes. For login purpose I have followed the steps as mentioned here.
My App.xaml.cs OnActivated function looks like this:
protected override void OnActivated(IActivatedEventArgs args)
{
base.OnActivated(args);
var protocolArgs = args as ProtocolActivatedEventArgs;
if (protocolArgs != null)
{
LifecycleHelper.FacebookAuthenticationReceived(protocolArgs);
}
Session.OnFacebookAuthenticationFinished += OnFacebookAuthenticationFinished;
}
and here is OnFacebookAuthenticationFinished method
private async void OnFacebookAuthenticationFinished(AccessTokenData session)
{
await Session.CheckAndExtendTokenIfNeeded();
if (Constant.fbSignup)
{
User fbUser = new User();
Account userAccount = new Account();
try
{
FacebookClient fbClient = new FacebookClient(session.AccessToken);
dynamic result = await fbClient.GetTaskAsync("me?fields=id,first_name,last_name,email,location");
fbUser.FirstName = result.first_name;
fbUser.LastName = result.last_name;
userAccount.UserName = result.email;
fbUser.UserAccount = userAccount;
//fbUser.City = result.location.name;
Constant.User = fbUser;
RootFrame.Navigate(typeof(SignUpPage));
}
catch (Exception ex)
{
await new MessageDialog(ex.Message).ShowAsync();
}
}
The login works fine.
Now I want to share some content using the Session.ShowFeedDialog(). I have followed the steps mentioned here for creating AppRequests within the dialog.
I am calling the ShowFeedDialog method this way from a page StoreDetailsPage.xaml.cs
All the following code rests in StorePageDetails.xaml.cs
Session.ShowFeedDialog("", link, linkDescription, linkCaption);
The posting also works fine. But I need to check whether the post was successful or not. For this purpose I tried the
Session.OnFacebookFeedFinished = Success;
where success is
public delegate void FacebookDelegate(FBResult result);
void Success(FBResult result)
{
//Code to check if post was successful
}
So my problem is after ShowFeedDialog is closed the OnActivated event is called and success delegate method is never reached or not called.
I haven't used delegates before so I don't know if there is something wrong there. Also I haven't figured out what the logic for post verification should since I was not able to step into this function. So any suggestions would be much appreciated

WinRT DownloadProgress callback Progress Status

I am writing a universal app primarily targeting Windows Phone using SQLite-net.
During the course of operation, the user is presented with an option to download multiple files. At the end of each file download, I need to mark the file in the db as completed. I am using BackgroundDownloader in order to download files - the WP8.0 app used Background Transfer Service and that worked great. Files can be huge (some 200+ mbs, user content) and i am not looking forward to wrapping the downloads in the HttpClient or WebClient.
However, it seems that the progress callback doesn't work with awaits unless I actually breakpoint in the method.
The following are listings from a sample app i quickly put together that demonstrates the behaviour:
Model:
public class Field
{
[PrimaryKey]
[AutoIncrement]
public int Id { get; set; }
public bool Done { get; set; }
}
MainPage codebehind (i am creating a db here only for the purposes of this example!):
private async void Button_Click(object sender, RoutedEventArgs e)
{
using (var db = new SQLiteConnection(Windows.Storage.ApplicationData.Current.LocalFolder.Path + "//Main.db"))
{
db.CreateTable<Field>();
db.Commit();
}
this.DbConnection = new SQLiteAsyncConnection(Windows.Storage.ApplicationData.Current.LocalFolder.Path + "//My.db");
var dl = new BackgroundDownloader();
dl.CostPolicy = BackgroundTransferCostPolicy.Always;
var transferUri = new Uri("http://192.168.1.4/hello.world", UriKind.Absolute);
var folder = await ApplicationData.Current.LocalFolder.CreateFolderAsync(
"Content",
CreationCollisionOption.OpenIfExists);
var localFile = await folder.CreateFileAsync("cheesecakes.file", CreationCollisionOption.ReplaceExisting);
var d = dl.CreateDownload(transferUri, localFile);
d.Priority = BackgroundTransferPriority.High;
var progressCallback = new Progress<DownloadOperation>(this.DownloadProgress);
await d.StartAsync().AsTask(progressCallback);
}
private async void DownloadProgress(DownloadOperation download)
{
Debug.WriteLine("Callback");
if (download.Progress.Status == BackgroundTransferStatus.Completed)
{
var f = new Field();
f.Done = true;
await this.DbConnection.InsertAsync(f);
Debug.WriteLine("DONE");
}
}
If i breakpoint inside the DownloadProgress and then press F5 i get both Debug messages, and my db gets a new record.
However, if i just let the code execute, i never see "DONE" printed to me and neither is my db updated.
I tried wrapping the code in a new task:
await Task.Run(
async () =>
{
Debug.WriteLine("taskrun");
.... OTHER CODE FROM ABOVE...
});
But again, i only get to see 'taskrun' if i breakpoint in the callback.
UPDATE I actually think this is more related to checking the status. E.g. the statements outside of the check are executed, but only once, whereas anything inside the check is not executed.
Is there any way to force that callback to be invoked when the download is completed?
private async void DownloadProgress(DownloadOperation download)
{
Debug.WriteLine("Callback");
var value = download.Progress.BytesReceived * 100 download.Progress.TotalBytesToReceive;
new System.Threading.ManualResetEvent(false).WaitOne(1000);
if (download.Progress.Status == BackgroundTransferStatus.Completed )
{
var f = new Field();
f.Done = true;
await this.DbConnection.InsertAsync(f);
Debug.WriteLine("DONE");
}
}
I had this problem too, and I solved this by sleeping for 1000 ms, which worked really well for me.
Not sure what is causing this, but I was able to get the sample app to work reliably by manually checking the bytes to download as opposed to relying on the DownloadOperation.Progress.Status:
private async void DownloadProgress(DownloadOperation download)
{
Debug.WriteLine("Callback");
var value = download.Progress.BytesReceived * 100 / download.Progress.TotalBytesToReceive;
if (download.Progress.Status == BackgroundTransferStatus.Completed || value >= 100)
{
var f = new Field();
f.Done = true;
await this.DbConnection.InsertAsync(f);
Debug.WriteLine("DONE");
}
This gets me to 'DONE' every time.

Categories

Resources