Do View-ViewModel be dispose when navigate - xamarin

I'm working in a Xamarin Forms project, using Prism DryIoc.
For example, i have a MainPage, and a APage, with ViewModels MainPageViewModel and APageViewModel. In APageViewModel i have a timer as simple as the code below:
private Timer _timer;
public override void Initialize(INavigationParameters parameters)
{
base.Initialize(parameters);
_timer = new Timer(1000);
_timer.Elapsed += (sender, args) =>
{
Debug.WriteLine($"{DateTime.Now.ToString(CultureInfo.InvariantCulture)}: Timer run");
};
_timer.Start();
}
When i navigate from MainPage to APage using NavigationService.PushAsync, the timer start. But when excute NavigationService.GoBackAsync from APageViewModel, the timer not stop if i dont manual stop the timer with _timer.Stop(). So i wonder if the APageViewModel being properly dispose to clear resources that being use in the view model?

Have you tried to stop the timer on the OnDisappearing method of the page?

Related

Xamarin.iOS - Unable to load view controller from within another view controller

I recently started developing using Xamarin, so I'm by no means an expert and have been stuck on this problem for a day or so now.
First of all, I am not using storyboards. I am creating my own custom views (xib) and loading them from code
I'm building a new Xamarin.iOS app and am attempting to load a view controller from within another view controller. Initially, I am loading the first controller from the AppDelegate like so:
public override bool FinishedLaunching(UIApplication application, NSDictionary launchOptions)
{
window = new UIWindow(UIScreen.MainScreen.Bounds);
appStartUpController = new AppStartUpController();
window.RootViewController = appStartUpController;
window.MakeKeyAndVisible();
return true;
}
This loads my AppStartUpController fine which is basically just a loading screen with a background image and loading animation while I make an API call in the background. Once the API call has completed, I want to load another view controller.
After the API call has completed, I attempt to load the next Controller like so:
var controller = new CityPickerViewController();
this.NavigationController.PushViewController(controller, false);
And here is my CityPickerViewController:
public partial class CityPickerViewController : UIViewController
{
CityPicker_View v;
public CityPickerViewController(IntPtr handle) : base(handle)
{
}
public CityPickerViewController ()
{
}
public override void ViewDidLoad()
{
base.ViewDidLoad();
v = CityPicker_View.Create();
this.View = v;
}
public override void ViewWillAppear(bool animated)
{
base.ViewWillAppear(false);
UIImage i = UIImage.FromFile("citypickbackground.jpg");
i = i.Scale(this.View.Frame.Size);
this.View.BackgroundColor = UIColor.FromPatternImage(i);
}
}
I'm probably missing something obvious here, but the CityPickerViewController will not load. If I put a break point within the code, the viewDidLoad / ViewWillAppear overrides never get hit.
I'm a rookie programmer and would definitely appreciate any tips on this. Thanks in advance!
Welcome to SO!
Try base.NavigationController.PushViewController(controller, true); instead since you don't have a local navigation controller.
There could also be an issue in your CityPickerViewController, so try a different ViewController if that doesn't work.

Xamarin Forms Map Viewable Area event handler

I have a Xamarin form map on my screen and I'm using PropertyChanged event to retrieve geolocation information from my server and display the proper pins on screen.
While coding the solution I noticed the PropertyChanged event is triggered multiple times (up to 10 times) with a single zoom or drag action on the map. This causes unnecessary calls to server which I want to avoid.
Ideally I want to make only one call to server when the final PropertyChanged event is called but I cant's find an easy solution to implement this.
At this point I've added a refresh button to my page that becomes enabled when a PropertyChanged event happens and I disable it after user uses the button.
Obviously this fixed the too many calls to server but made the solution manual.
I was wondering if there is a more elegant way to make the server call but do it automatically.
Thanks in advance.
I just test the PropertyChanged event on iOS side and it just triggered one time with a single zoom or drag action on the map.
While if it really triggered multiple times, you can use a timer to call the server when the final PropertyChanged event is called, for example:
public partial class MapPage : ContentPage
{
Timer aTimer;
public MapPage()
{
InitializeComponent();
customMap.PropertyChanged += CustomMap_PropertyChanged;
}
private void CustomMap_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
if (aTimer != null)
{
aTimer.Enabled = false;
aTimer.Stop();
aTimer.Close();
}
aTimer = new Timer();
aTimer.Interval = 1000;
aTimer.Enabled = true;
aTimer.Elapsed += ATimer_Elapsed;
aTimer.Start();
}
private void ATimer_Elapsed(object sender, ElapsedEventArgs e)
{
aTimer.Stop();
//do web request
Console.WriteLine(sender);
Console.WriteLine("CustomMap_PropertyChanged");
}
}
In the above code, I set the Interval = 1 second, that means in 1 second, whatever how many times PropertyChanged triggered, only the last call will trigger the ATimer_Elapsed function.
The Interval can be set to any value depending on your requirement.

Activity Spinner in Xamarin doesn't instantly update

So I have this problem...
I have an activity Indicator in Xamarin set up like this
<ActivityIndicator IsRunning="{Binding IsSearching}" />
In my page class I bind it up like this...
private BindableProperty IsSearchingProperty =
BindableProperty.Create("IsSearching", typeof(bool), typeof(MainPage), false);
public bool IsSearching
{
get { return (bool)GetValue(IsSearchingProperty); }
set
{
SetValue(IsSearchingProperty, value) ;
}
}
Everythig works but only one problem. The spinner won't initialize before the method from which it is called is not finished... For example...
private async void New_Survey_Button_OnClicked(object sender, EventArgs e)
{
IsSearching = true;
some method that takes a long time...
await Navigation.PushAsync(new SurveyNavigationPage());
}
In this case the IsSearching true value will get initialized after the compiler hits await. Truly stuck with this issue. Please help. Thank you.
UPDATE 1___________________________
So after some debugging here is what exactly is going on.
1) IsSearching is hit which takes the program to the setter.
2) After it hits the setter it continues to the method that takes a long time.
3) Only after it is done with the time consuming method and goes out of the New_Survey_Button_OnClicked it actually hits the getter and updates the UI.

A while after I deploy my code to iOS the phone hangs up

Is there some way I can track what's happening with Xamarin? I do a debug with a target of my phone and then later it hangs up. I can't do anything, can't shut it down with the button on the side and the only way I can get the phone to work again is by pressing the button on the side and the home button. Running on iPhone 6s Plus.
Here is some code that I suspect might be causing a problem. Would also like to know if anyone can see anything that might cause the problem with the code:
public partial class App : Application
{
public static DataManager db;
private static Stopwatch stopWatch = new Stopwatch();
private const int defaultTimespan = 1;
public App()
{
InitializeComponent();
}
public static DataManager DB
{
get
{
if (db == null)
{
db = new DataManager();
}
return db;
}
}
protected override void OnStart()
{
App.DB.InitData();
MainPage = new Japanese.MainPage();
if (!stopWatch.IsRunning)
stopWatch.Start();
Device.StartTimer(new TimeSpan(0, 0, 1), () =>
{
if (stopWatch.IsRunning && stopWatch.Elapsed.Minutes >= defaultTimespan)
{
Debug.WriteLine("Checking database");
PointChecker.CheckScore();
stopWatch.Restart();
}
return true;
});
}
protected override void OnSleep()
{
Debug.WriteLine("OnSleep");
stopWatch.Reset();
}
protected override void OnResume()
{
Debug.WriteLine("OnResume");
// deductPoints();
stopWatch.Start();
}
}
iOS requires that everything is setup, with 17 seconds, on the initial first load. This means that you must set the MainPage in your App constructor, you can't set it in OnStart.
Or, you can place MainPage = new ContentPage(); in your App constructor, then it will be replaced in OnStart. However, you must set the MainPage, when it's constructing the Application.
Android and UWP I think, give you some freedom, and you can set it in OnStart, but definitely not iOS.
My iPhones are hangs up when I have debugger connected to running app and that connection is interrupted. For example, if you unplug lightning cable while Visual Studio is debugging - the phone will hangs.
So try to start your application from phone(without debugger attached) and check your datacable.

Start specific view of Gluon App from a notification

I set up an alarm to show a corresponding Notification. The PendingIntent of the Notification is used to start the Gluon App main class. To show a View other than the homeView, I call switchView(otherView) in the postInit method. OtherView is shown, but without AppBar. While it's possible to make the AppBar appear, I wonder if this is the right approach.
#Override
public void postInit(Scene scene) {
// additional setUp logic
boolean showReadingView = (boolean) PlatformProvider.getPlatform().getLaunchIntentExtra("showReadingView", false);
if (showReadingView) {
switchView(READING_VIEW);
}
}
When triggering anything related to the JavaFX thread from another thread, we have to use Platform.runLater().
Yours is a clear case of this situation: the Android thread is calling some pending intent, and as a result, the app is started again.
This should be done:
#Override
public void postInit(Scene scene) {
// additional setUp logic
boolean showReadingView = (boolean) PlatformProvider.getPlatform().getLaunchIntentExtra("showReadingView", false);
if (showReadingView) {
Platform.runLater(() -> switchView(READING_VIEW));
}
}

Resources