FreshMvvm - PopPageModel not works on Android - mvvm

I have a Xamarin.Forms app and I am using FreshMvvm framework.
If I do this from ViewIsAppearing method of FirstPageModel:
CoreMethods.PushPageModel<SecondPageModel>();
I go the "SecondPageModel". Then, when I am in the "SecondPageModel" if I do:
CoreMethods.PopPageModel();
or press hard back button, or press title bar back button not works in Android (anything happens). I am using FreshMasterDetailNavigationContainer.
In iOS it works OK, I get back to FirstPageModel.

This is because ViewIsAppearing will always be called when the page starts displaying on the screen. When you pop the second page then go to the first page, the first page's ViewIsAppearing will fire again. It caused a dead cycle and prohibited your app from returning to the first page.
Add a property to avoid that:
bool isInitialized;
public FirstPageModel()
{
// ...
isInitialized = true;
}
protected async override void ViewIsAppearing(object sender, EventArgs e)
{
base.ViewIsAppearing(sender, e);
if (isInitialized)
{
await Task.Delay(100);
await CoreMethods.PushPageModel<SecondPageModel>();
isInitialized = false;
}
}
iOS may optimize this process, but I still recommend you to add this judgment statement.
Update:
Call it when your app has reached the main thread.
protected override void ViewIsAppearing(object sender, EventArgs e)
{
base.ViewIsAppearing(sender, e);
if (isInitialized)
{
Device.BeginInvokeOnMainThread(() =>
{
CoreMethods.PushPageModel<SecondPageModel>();
isInitialized = false;
});
}
}

Related

WinUI3: display dialog at startup from main window?

I'm getting started with WinUI3. Having no previous experience with UWP or WPF or even much C#, it's tough going.
Today's question is how to display a simple dialog at startup. Consider that we start with a simple app, as generated by Visual Studio. We have a MainWindow class defined in MainWindow.xaml.cs and its associated XAML (MainWindow.xaml). I believe the class is called a code-behind class.
So I want to do something as simple as display a dialog when the (desktop) app runs. It looks as though a ContentDialog is the way to go. But how to display it? As I understand it, I'm going to need to set the XamlRoot, so naively I tried this:
public MainWindow()
{
this.InitializeComponent();
DisplayDialog();
}
private async void DisplayDialog()
{
var dlg = new ContentDialog();
dlg.XamlRoot = this.Content.XamlRoot; // <-- set the XAML root here, but it's null
dlg.Content = "Hello World";
await dlg.ShowAsync();
}
This doesn't work. When called, the main window's XAML root is null and trying to show the dialog throws an exception:
How do I detect when it's ok to use the main window's XAML root? This issue seems to hint at an OnLoaded event, but I can't find anything about OnLoaded events in WinUI. Did I miss something?
This only way I can get this to work is to hook into the window's button and respond to its Loaded event, i.e.
public MainWindow()
{
this.InitializeComponent();
myButton.Loaded += MyButton_Loaded; // <-- hack
}
private void MyButton_Loaded(object sender, RoutedEventArgs e)
{
DisplayDialog();
}
private async void DisplayDialog()
{
var dlg = new ContentDialog();
dlg.XamlRoot = this.Content.XamlRoot; // <-- this is non-null now!
dlg.Content = "Hello World";
await dlg.ShowAsync();
}
But this feels really dirty. I don't even know if it's guaranteed that the XamlRoot will be non-null just because a button has loaded. And anyway, latching onto the button load seems very much like a hack. It relies on there being a button for one thing!
So how should I achieve the simple task of putting a dialog on the screen when all I have is the main window?
All help very gratefully received. Please try to make any answers as newbie-friendly as possible.
Unfortunately, the MainWindow has no Loaded events. What you can do is to work with a Page instead. So basically use the MainWindow as a "Window" and use Pages for your contents.
MainPage.xaml.cs
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
this.Loaded += MainPage_Loaded;
}
private async void MainPage_Loaded(object sender, RoutedEventArgs e)
{
var dlg = new ContentDialog();
dlg.XamlRoot = this.XamlRoot;
dlg.Content = "Hello World";
await dlg.ShowAsync();
}
}
MainWindow.xaml
<Window
x:Class="ContentDialogs.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="using:ContentDialogs"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<local:MainPage />
</Window>
A WinUI Window is just an abstraction of each of the low-level window implementations used by supported (UWP and desktop) app models.
The "trick" is to handle the Loaded event of the window's root element. The default template includes a Grid root element. A "generic" solution could be implemented something like this:
public MainWindow()
{
this.InitializeComponent();
var root = this.Content as FrameworkElement;
if (root != null)
root.Loaded += async (s, e) => await DisplayDialog();
}
private async Task DisplayDialog()
{
var dlg = new ContentDialog();
dlg.XamlRoot = this.Content.XamlRoot;
dlg.Content = "Hello World";
await dlg.ShowAsync();
}
Also tote that an async method, with the exception of event handlers, should return a Task or a Task<T> and be awaited by the caller.

How to use WebView.Reload() within SizeChanged event in .NET MAUI?

I need to refresh webView control when the ui size changed. I used SizeChanged event in some controls like Border, WebView, etc SizeChanged event. But I got this system.invalidoperationexception 'a method was called at an unexpected time error. I got this error at the beginning of running the application. The way I used:
<WebView x:Name="webView" SizeChanged="OnSizeChanged" HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand">
<WebView.Source>
...
</WebView.Source>
</WebView>
private void OnSizeChanged(object sender, EventArgs e)
{
webView.Reload();
}
Probably I used it in wrong way, or could it be a bug?
I solved the problem when I used Reload method in try-catch:
private void OnSizeChanged(object sender, EventArgs e)
{
try
{
webView.Reload();
}
catch(Exception ex)
{
}
}

Xamarin Forms Application Crashes without log

I'm developing some application using Xamarin Forms that has a function of route following. When it is used in real conditions on a vehicle it crashes whithout any logs in spite of the fact that I'm using AppCenter, i.e. in App.xaml.cs OnStart I added
protected async override void OnStart()
{
AppCenter.Start("android=__mycode___" +
"uwp={Your UWP App secret here};" +
"ios={Your iOS App secret here}",
typeof(Analytics), typeof(Crashes));
bool hadMemoryWarning = await Crashes.HasReceivedMemoryWarningInLastSessionAsync();
ErrorReport crashReport = await Crashes.GetLastSessionCrashReportAsync();
if (crashReport != null)
{
Analytics.TrackEvent(crashReport.StackTrace);
}
}
In fact I tested my app in AppCenter by using Crashes.GenerateTestCrash() and got a test crash report. Besides I caught some errors using this approach. But now there is at least one reason that makes my app crash without any messages to AppCenter. Are there any clues how to resolve this problem?
Try using an actual asynchronous event handler.
private event EventHandler onStart = delegate { };
protected override void OnStart() {
onStart += handleStart; //subscribe
onStart(this, EventArgs.Empty); //raise event
}
private async void handleStart(object sender,EventArgs args) {
onStart -= handleStart; //unsubscribe
try {
AppCenter.Start("android=__mycode___" +
"uwp={Your UWP App secret here};" +
"ios={Your iOS App secret here}",
typeof(Analytics), typeof(Crashes));
bool hadMemoryWarning = await Crashes.HasReceivedMemoryWarningInLastSessionAsync();
ErrorReport crashReport = await Crashes.GetLastSessionCrashReportAsync();
if (crashReport != null) {
Analytics.TrackEvent(crashReport.StackTrace);
}
} catch (Exception ex) {
//...handle exception or log as needed
}
}
OnStart is a simple void method and not an actual event handler. This mean that when made async it will become a fire and forget function which wont allow you to catch any exceptions that may have occurred within it.
If it is not caught at startup then it probably means that you are doing something similar somewhere within the app using a non event async void that goes uncaught and is crashing the application.
Start by doing a search for any async void in your code and checking to make sure that it is an actual event handler.

Zxing barcode scanner continious scanning problem

I'm using Zxing barcode scanner and Rg Pop up pages in Xamarin forms. The whole idea is that when the camera catches a barcode it displays a pop up. The problem is that I want to wait for the user to close the pop up before it scans again. Now if I keep the camera towards a barcode it keeps showing multiple alerts.
public async void Scan(Result result)
{
//random linq to get product
if (product != null)
{
await PopupNavigation.PushAsync(new DisplayDialog(product));
}
}
Method for when user closes the window
private async void Button_OnClicked(object sender, EventArgs e)
{
await PopupNavigation.RemovePageAsync(this);
}
I tried using a flag to stop the scan and setting isScanning or isAnalyzing to false but it didnt work. Any ideas? Thank you!
use a bool to control the flow
bool popup = false;
public async void Scan(Result result)
{
if (popup) return;
//random linq to get product
if (product != null)
{
popup = true;
await PopupNavigation.PushAsync(new DisplayDialog(product));
}
}
then whenever your popup is dismissed, reset the popup flag to false

Page.OnAppearing() Firing Twice

When I push a new instance of my page onto the navigation stack, OnAppearing() fires twice and consequently two DeliveryNotePicker pages are created. There's nothing in the call stack that gives me any clues. Why might this be happening?
protected override async void OnAppearing()
{
base.OnAppearing();
MessagingCenter.Subscribe<ScannerMessages, Barcode>(this, "ScannerData", (sender, arg) =>
{
Device.BeginInvokeOnMainThread(() => { ItemScanned(arg.Value); });
});
try
{
if (picklist == null)
{
// Attempt to retrieve an existing picklist:
picklist = (List<Pick>)Application.Current.Properties[PicklistProperty];
branchName.Text = (string)Application.Current.Properties[BranchNameProperty];
NextPick();
}
}
catch (KeyNotFoundException)
{
// Create a new picklist:
await Navigation.PushModalAsync(new DeliveryNotePicker());
}
}
I've also encountered this bug, think it's caused when pushing a new modal within the OnAppearing function
Workaround is to either check the current page isn't the same as in Saamer's last comment or using a private counter to check number of opens