I have a class SplashScreen where I show an image scaling and after it finishes scaling it goes to a login page. I tried this on multiple devices and only in one device it doesn't change from the SplashScreen.
I've been researching how to handle async methods and await instructions but nothing seems to work so far. Also I tried removing the ScaleTo and just show the image but it doesn't work.
This is what I have:
protected override async void OnAppearing()
{
base.OnAppearing();
await splashLogo.ScaleTo(1.5, 3000);
ShowLogin();
}
I solved this problem by adding this class:
class MainPageViewModel : BaseViewModel
{
private bool isLoadingData;
public bool IsLoadingData
{
get => isLoadingData;
set => SetProperty(ref isLoadingData, value);
}
public async Task LoadData()
{
IsLoadingData = true;
await Task.Delay(2000);
IsLoadingData = false;
}
}
Then in my SplashScreen page:
public SplashScreen()
{
//Everything else I need in this page
BindingContext = new MainPageViewModel();
}
private MainPageViewModel ViewModel => BindingContext as MainPageViewModel;
protected override async void OnAppearing()
{
base.OnAppearing();
await ViewModel.LoadData();
ShowLogin();
}
Related
How to put an ActivityIndicator on Xamarin Forms OnStart() function.
I am check Network access on OnStart() function.
Bind the ActivityIndicator to a property in your BaseViewModel (IsBusy).
View
<ActivityIndicator Color="Accent" IsVisible="{Binding IsBusy}" IsRunning="{Binding IsBusy}" />
BaseViewModel (Inherited by all ViewModels)
private bool _isBusy;
public bool IsBusy
{
get { return _isBusy; }
set
{
_isBusy = value;
OnPropertyChanged("IsBusy");
}
}
Get yourself a good MVVM framework (Prism) and put the network check in the OnNavigatedTo method for your start page.
public override void OnNavigatedTo(INavigationParameters parameters)
{
IsBusy = true;
await CheckNetwork();
IsBusy = false;
}
Now you can paste that same ActivityIndicator snippet into any page (XAML) that is bound to a ViewModel inheriting BaseViewModel and it will just work when you set IsBusy.
Haven't used ActivityIndicator, but this nuget works great: Acr.UserDialogs.
After installing and adding the initialization part in the MainActivity or ios equivalent, just add the following code between resource intensive threads in either your code-behind file or viewmodel (mvvm):
This works for code-behind file:
protected override async void OnAppearing(object sender, EventArgs e)
{
base.ViewIsAppearing(sender, e);
UserDialogs.Instance.ShowLoading();
//do stuff here
UserDialogs.Instance.HideLoading();
}
This works for FreshMVVM framework:
protected override async void ViewIsAppearing(object sender, EventArgs e)
{
base.ViewIsAppearing(sender, e);
UserDialogs.Instance.ShowLoading();
//do stuff here
UserDialogs.Instance.HideLoading();
}
I'm using network checking in my projects too, please check this:
using Plugin.Connectivity;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
namespace PetBellies.View
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class NoConnection : ContentPage
{
private bool wasNotConn = false;
public NoConnection()
{
InitializeComponent();
CrossConnectivity.Current.ConnectivityChanged += async (sender, args) =>
{
if (CrossConnectivity.Current.IsConnected && !wasNotConn)
{
wasNotConn = true;
await Navigation.PushModalAsync(new NavigationPage(new MainPage()));
}
else
{
wasNotConn = false;
}
};
}
public NoConnection(bool isFromLogin)
{
CrossConnectivity.Current.ConnectivityChanged += async (sender, args) =>
{
if (CrossConnectivity.Current.IsConnected && !wasNotConn)
{
wasNotConn = true;
var page = new LoginPage();
var navPage = new NavigationPage(page);
NavigationPage.SetHasNavigationBar(navPage, false);
await Navigation.PushModalAsync(navPage);
}
else
{
wasNotConn = false;
}
};
}
}
}
https://github.com/officialdoniald/PetBellies/blob/master/PetBellies/PetBellies/View/NoConnection.xaml.cs
If the connection lost, the application navigate to this page and stay on this page while the connection is unavailable.
I have a ListView that I am updating every 5 seconds using Device.StartTimer() and I would like to stop the timer when it leaves the ViewModel page. as you must intuit necsito do this because Device.StartTimer () is global and even when I change the page is still updating my ListView, how can I make ViewModel know that I'm changing pages?
This is part of my ViewModel:
private ObservableCollection sensors;
public ObservableCollection<PcData> Sensors
{
get { return sensors; }
set
{
sensors = value;
OnPropertyChanged();
}
}
public MonitoringTabsViewModel(string idCode, string description)
{
Description = description;
LoadSensors(idCode);
Device.StartTimer(TimeSpan.FromSeconds(5), () =>
{
RefreshSensors(idCode);
return true;
});
}
private async void LoadSensors(string idCode)
{
Sensors = new ObservableCollection<PcData>(await App.WebApiManager.GetCurrentStatusDeviceAsync(idCode));
}
private async void RefreshSensors(string idCode)
{
Sensors = null;
Sensors = new ObservableCollection<PcData>(await App.WebApiManager.GetCurrentStatusDeviceAsync(idCode));
}
In the end I have come to the following implementation which actually does what I wanted:
ViewModel:
public class MonitoringTabsViewModel : Notificable
{
public string IdCode { get; set; }
public bool InPage { get; set; }
private string description;
public string Description
{
get { return description; }
set
{
description = value;
OnPropertyChanged();
}
}
private ObservableCollection<PcData> sensors;
public ObservableCollection<PcData> Sensors
{
get { return sensors; }
set
{
sensors = value;
OnPropertyChanged();
}
}
public MonitoringTabsViewModel(string idCode, string description)
{
IdCode = idCode;
Description = description;
LoadSensors(idCode);
MessagingCenter.Subscribe<MonitoringView>(this, "OnAppearing", (sender) =>
{
InPage = true;
});
MessagingCenter.Subscribe<MonitoringView>(this, "OnDisAppearing", (sender) =>
{
InPage = false;
});
Device.StartTimer(TimeSpan.FromSeconds(5), TimerCallBack);
}
private bool TimerCallBack()
{
if (InPage)
{
RefreshSensors(IdCode);
MessagingCenter.Unsubscribe<MonitoringView>(this, "OnAppearing");
return true;
}
else
{
MessagingCenter.Unsubscribe<MonitoringView>(this, "OnDisAppearing");
return false;
}
}
private async void LoadSensors(string idCode)
{
Sensors = new ObservableCollection<PcData>(await App.WebApiManager.GetCurrentStatusDeviceAsync(idCode));
}
private async void RefreshSensors(string idCode)
{
Sensors = null;
Sensors = new ObservableCollection<PcData>(await App.WebApiManager.GetCurrentStatusDeviceAsync(idCode));
}
}
View:
protected override void OnAppearing()
{
base.OnAppearing();
MessagingCenter.Send<MonitoringView>(this, "OnAppearing");
}
protected override void OnDisappearing()
{
base.OnDisappearing();
MessagingCenter.Send<MonitoringView>(this, "OnDisAppearing");
}
There are still two things that concern me:
1. I do not know if the management I'm giving to the MessagingCenter is appropriate, as you can see I'm unsubscribing in my TimerCallBack method, by putting breakpoints in the two calls to the unsubscribe method I see that while the timer is running every 5 seconds The unsubscribe method of the onAppearing message is still called.
2. Although this implmentacion works, I still have the problem that when sleeping the application or put it in the background is still running my method RefreshSensors () and I would like to be in segudno flat also stop the execution.
Could someone give me ideas of these two concerns that I still have?
Page has 2 indicator methods OnAppearing() & OnDisappearing() depends on your setup you should hookup to this events and notify the ViewModel.
This can be done in multiple ways:
Page may have a direct or indirect reference (BindingContext) to the ViewModel so just hookup.
You can use MessagingCenter.
If you have a custom handmade NavigationService you could hookup there.
Use existing MVVM Framework, there are plenty of them and most of them support this scenario
I still have the problem that when sleeping the application or put it
in the background is still running my method RefreshSensors ()
If you look in you App.xaml.cs file, you'll find the following methods:
protected override void OnStart()
{
// Handle when your app starts
}
protected override void OnSleep()
{
// Handle when your app sleeps
}
protected override void OnResume()
{
// Handle when your app resumes
}
I'm doing a Master Detail Page as a menu and I'm getting this error:
PushAsync is not supported globally on Android, please use a NavigationPage.
When I try to instantiate another page. I believe my code already creates a navigation page when instantiate the page, but I'm not sure. Can someone help me?
App.xaml.cs:
public partial class App : Application
{
static public MasterDetailPage MasterDetail { get; set; }
public async static Task NavigateMasterDetail(Page page)
{
App.MasterDetail.IsPresented = false;
await App.MasterDetail.Navigation.PushAsync(page);
}
public App ()
{
InitializeComponent();
MainPage = new selectPage();
}
Page that carry the menu, selectPage:
public partial class selectPage : MasterDetailPage
{
public selectPage()
{
InitializeComponent();
this.Master = new Master();
this.Detail = new NavigationPage(new Detail());
App.MasterDetail = this;
}
}
Master.xaml.cs:
public Master ()
{
InitializeComponent ();
toDivPage.Clicked += async (sender, e) =>
{
await App.NavigateMasterDetail(new MainPage());
};
toBiqPage.Clicked += async (sender, e) =>
{
await App.NavigateMasterDetail(new MainPage());
};
}
Detail.xaml.cs is empty.
I believe you have to use NavigationPage in your App.xaml.cs.
In your App.xaml.cs make sure your constructor or App method looks like this
public App ()
{
InitializeComponent();
MainPage = new NavigationPage(new selectPage());
}
I hope this helps
I am not able to find out how to navigate from one screen to another automatically without having to click on any button or perform any action. I want to start another screen just after my splash screen shows up. As i am new on this tool or technology. Please help. Thank you in advance.
My code is:
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
}
protected async override void OnAppearing()
{
base.OnAppearing();
await Task.Delay(5000); // Simulate a bit of startup work.
await this.Navigation.PushAsync(new HomePage());
}
}
Working and tested code:
protected override void OnAppearing()
{
base.OnAppearing();
GoToMaster();
}
private async void GoToMaster()
{
await Task.Delay(5000);
await this.Navigation.PushAsync(new SignUpPage());
}
I made my splash activity under xamarin.droid file and i want it to display the xaml file that i made in portable portion before launching the home page activity.
You can't skip the MainActivity as it is the container of your pages in Portable lib, So if you want to redirect to your xaml page after the splash screen, you can make SplashScreenActivity navigate to your MainActivity and make your xaml file the MainPage of App.cs:
public partial class App : Application
{
public App()
{
InitializeComponent();
MainPage = new YourPage();
}
....
}
And in your SplashScreenActivity you need to navigate to the MainActivity:
[Activity(Theme = "#style/MyTheme.Splash", MainLauncher = true, NoHistory = true)]
public class SplashActivity : AppCompatActivity
{
static readonly string TAG = "X:" + typeof(SplashActivity).Name;
public override void OnCreate(Bundle savedInstanceState, PersistableBundle persistentState)
{
base.OnCreate(savedInstanceState, persistentState);
}
protected override void OnResume()
{
base.OnResume();
Task startupWork = new Task(() => { SimulateStartup(); });
startupWork.Start();
}
async void SimulateStartup()
{
await Task.Delay(8000);
StartActivity(new Intent(Application.Context, typeof(MainActivity)));
}
}
here is the complete demo:SplashScreenDemo.
I didn't know how better to word the title so I went with solution that came to my mind.
Here is the problem. I have a page that has list and each item on the lists opens a detail page (on click). But the VM is reused, which causes me several problems.
Previous data can be seen for split second when opening a the detail page
I need certain properties to be set to specific values when the page open, but since the VM is reused it keeps all the values from the previous detail and this messes up my logic.
This UWP app. I'm using Template10 framework's NavigationService to move between pages.
Main Page ViewModel
public class MainPageViewModel : ViewModelBase {
private List<MangaItem> _mangaList;
public List<MangaItem> mangaList {
get { return _mangaList; }
set { Set(ref _mangaList, value); }
}
private string _mainSearchText;
public string mainSearchText {
get { return _mainSearchText; }
set { Set(ref _mainSearchText, value); }
}
public MainPageViewModel() {
_mangaList = new List<MangaItem>();
mangaList = new List<MangaItem>();
Initialize();
}
private async void Initialize() {
mangaList = await MangaListGet.GetListAsync();
}
public async void MainSearchSubmitted() {
mangaList = await MangaListGet.GetListAsync(_mainSearchText);
}
public void MangaSelected(object sender, ItemClickEventArgs e) {
var mangaItem = (MangaItem)e.ClickedItem;
NavigationService.Navigate(typeof(Views.MangaDetail), mangaItem.id);
}
}
And Detail Page ViewModel
class MangaDetailViewModel : ViewModelBase {
private MangaItem _mangaDetail;
public MangaItem mangaDetail {
get { return _mangaDetail; }
set { Set(ref _mangaDetail, value); }
}
private string _mangaId;
public override async Task OnNavigatedToAsync(object parameter, NavigationMode mode, IDictionary<string, object> suspensionState) {
_mangaId = parameter as string;
Initialize();
await Task.CompletedTask;
}
private async void Initialize() {
mangaDetail = await MangaDetailGet.GetAsync(_mangaId);
}
public void ChapterSelected(object sender, ItemClickEventArgs e) {
var _chapterId = (ChapterListItem)e.ClickedItem;
NavigationService.Navigate(typeof(Views.ChapterPage), _chapterId.id);
}
}
This code only shows the first problem is displaying previously loaded data for a split second. If needed I will add code that showcases the other problem, but I' not sure if it's really relevant right now. I'm thinking that maybe my entire logic is flawed or something.
EDIT:
<Page.DataContext>
<vm:ChapterPageViewModel x:Name="ViewModel" />
</Page.DataContext>
where vm is xmlns:vm="using:MangaReader.ViewModels".
Another solution is to use Bootstrapper.ResolveforPage() which is intended to handle dependency injection but would easily serve your needs. Like this:
[Bindable]
sealed partial class App : BootStrapper
{
static ViewModels.DetailPageViewModel _reusedDetailPageViewModel;
public override INavigable ResolveForPage(Page page, NavigationService navigationService)
{
if (page.GetType() == typeof(Views.DetailPage))
{
if (_reusedDetailPageViewModel == null)
{
_reusedDetailPageViewModel = new ViewModels.DetailPageViewModel();
}
return _reusedDetailPageViewModel;
}
else
{
return null;
}
}
}
The NavigationService will treat this the same as any other view-model. Meaning it will call OnNavTo() and the other navigation overrides you include.
Best of luck.
While Template10 documentation states the NavigationCacheMode is disabled by default, that isn't the case in it's example templates (as of writing this). This is set in View C# code (.xaml.cs file).
.xaml.cs file
namespace MangaReader.Views {
public sealed partial class MangaDetail : Page {
public MangaDetail() {
InitializeComponent();
//NavigationCacheMode = Windows.UI.Xaml.Navigation.NavigationCacheMode.Enabled; //this was set by default
NavigationCacheMode = Windows.UI.Xaml.Navigation.NavigationCacheMode.Disabled;
}
}
}
Now, new ViewModel will be created each time you access a this page.