Navigation on ViewModel in MVVM Light in Xamarin.Forms - xamarin

I have a Xamarin.froms app in MVVM Light, i want to open another page on button click, but i only want to use this method of Page class
page.Navigation.PushModalAsync(new MyPage())
How to send "Page" class as reference on ViewModel class.

There are variety of ways to do this.
Easy way:
Pass the Page as a constructor parameter of View Model.
The useful way:
Write a Navigation Service. This can extend from a Content Page like this:
public class NavigationService : ContentPage
{
public static INavigation Navigation
{
get {
return Application.Current.MainPage.Navigation;
}
}
public static IReadOnlyList<Page> NavigationStack () {
return Navigation.NavigationStack;
}
}
Now you can use this Service in your ViewModel like this:
Page lastPage = Navigation.NavigationStack.Last;

XAML code
<Button Text="Help" Command="{Binding NavigateHelpPage}"/>
MVVM Hierarchical page navigation in Xamarin.forms
public ICommand NavigateHelpPage { get; }
private async Task NavigateToHelpPage()
{
await Application.Current.MainPage.Navigation.PushAsync(new HelpPage());
}
Inside Constructor:
NavigateHelpPage = new Command(async () => await NavigateToHelpPage());
MainPage you can set like :
Application.Current.MainPage = new NavigationPage(new Views.Dashboard.DashboardPage());

Related

UI freezes for 2-3 seconds when loading a carouselview

I have a carouselview binded to a viewmodel, on the previous page (call it first page) user can select 2 arguments and with the help of those, the next view (call it second page) is generated accordingly. However, I can't wrap my head around why my view won't load asynchronously.
So my problem: When I click the button on the first page the UI would freeze for like a solid 2-3 seconds, and then start load (asynchronously?) and once it's done it's all good.
Also I couldn't really figure out a better way to inherit values from first page to second so if someone has an idea please let me know.
Any help on how can I fix this I would really appreciate.
The viewmodel for second page
public class DataSelectionViewModel : BaseViewModel
{
public ObservableCollection<Items> FilteredData { get; set; }
public UserSelectionViewModel()
{
_dataStore = DependencyService.Get<IDataStore>();
LoadData= new AsyncAwaitBestPractices.MVVM.AsyncCommand(FilterData);
FilteredData = new ObservableRangeCollection<Items>();
}
public async Task FilterData()
{
FilteredData.Clear();
var filtereddata = await _dataStore.SearchData(Hard, Subject).ConfigureAwait(false);
foreach (var data in filtereddata)
{
FilteredData.Add(data);
}
}
}
The carouselview in second page
<ContentPage.BindingContext>
<db:DataSelectionViewModel/>
</ContentPage.BindingContext>
...
<!-- DataTemplate for carouselview has radiobuttons, label and button all in a grid -->
<CarouselView ItemsSource="{Binding FilteredData}">
Second Page c#
public partial class SecondPage : ContentPage
{
public Coll(bool hard, string subject)
{
InitializeComponent();
var vm = (BaseViewModel)BindingContext;
vm.Hard = hard;
vm.Subject = subject;
/* had to set "hard" and "subject" here again, otherwise data won't load */
}
protected override async void OnAppearing()
{
var vm = (DataSelectionViewModel)BindingContext;
base.OnAppearing();
await vm.LoadData.ExecuteAsync().ConfigureAwait(false);
}
}
The first page view containing the button
<Button x:Name="Start" Pressed="ButtonClick"/>
First page c# --> Here I also tried doing it with a command and a pressed at the same time, because I couldn't come up with a way to save variables to second page viewmodel, that's why I use pressed here
private async void ButtonClick(object sender, EventArgs e)
{
var vm = (BaseViewModel)BindingContext;
vm.Hard = HardButtonSelected == Hard;
vm.Subject = vm.Subject.ToLower();
await Navigation.PushAsync(new SecondPage(vm.Hard, vm.Subject));
}
I have tried not using the OnAppearing method to get my data, but then it wouldn't bind to the page and it would not show, if I were to previously fill the ObservableCollection with my data and then load the page although I would love to be able to do this because it would allow me to create a loading popup also.

Xamarin Iconize IconTabbedPage Example

Can someone provide an example of how to use the IconTabbedPage in Iconize, preferably in Xaml? I have an IconTabbedPage with IconNavigation pages as children, all defined in Xaml. I then set the Icon property of the subpages by specifiying the font awesome name (“fa-home”). I tried to set the title as well, but neither of these will render the icon. I have search (a lot) for examples of the IconTabbedPage but couldn’t find any in Xaml. Additional bonus if you can provide an example of how to use the icons in a list cell context action.
Looking into #Niklas Code, you can create a tabbed page with a base class that inherits from IconTabbedPage , then your xaml will look like this.
<icon:IconTabbedPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:icon="clr-namespace:Plugin.Iconize;assembly=Plugin.Iconize"
....
>
<icon:IconTabbedPage.Children>
<ContentPage Title="Build" Icon="md-build">
</ContentPage>
</icon:IconTabbedPage.Children>
I hope it will help somebody
I think you can take a look on Sample on GitHub
[\[assembly: XamlCompilation(XamlCompilationOptions.Compile)\]
namespace Iconize.FormsSample
{
public class App : Application
{
public App()
{
// The root page of your application
var tabbedPage = new IconTabbedPage { Title = "Iconize" };
foreach (var module in Plugin.Iconize.Iconize.Modules)
{
tabbedPage.Children.Add(new Page1
{
BindingContext = new ModuleWrapper(module),
Icon = module.Keys.FirstOrDefault()
});
}
MainPage = new IconNavigationPage(tabbedPage);
}
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
}
}
}][1]

What is the different between Navigation.PushModalAsync() andNavigation.PushAsync() In Xamarin Forms?

Reason for ask this i got error when i use PushAsync in xamarin forms mvvm architecture
await Navigation.PushAsync(new Page2());
Error is
I have corrected this error by changing PushAsync to PushModelAsync
await Navigation.PushModalAsync(new Page2());
anyone has idea about what cause to this error?
i am calling PushModelAsync from ViewModel
public class Page1Model : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public INavigation Navigation { get; set; }
public Command ContinueBtnClicked { get; }
public Page1Model(INavigation navigation)
{
this.Navigation = navigation;
this.ContinueBtnClicked = new Command(async () => await GotoPage2());
}
public async Task GotoPage2()
{
await Navigation.PushModalAsync(new Page2());
}
}
In your App.xaml.cs wrap your first page in a NavigationPage, like this: MainPage = new NavigationPage(new Page1());.
Now it will work. If you just have a single page, iOS doesn't know how to navigate to another page from there, so it needs a container page to handle navigation.
The reason the the modal variant works is because it spawns a new navigation stack and shows pages on top of everything else (hence modal).

Pushing content page from background thread freezes app

In Xamarin Forms for iOS, I have a custom renderer for a ContentPage that displays a video control. In my Xamarin Forms app, this custom ContentPage is displayed inside a NavigationPage.
I would like to have the video screen open when a specific message comes in via MQTT.
When I open the video page by clicking a link on the main screen, it opens as expected. I know I am receiving the message via MQTT and calling Navigation.PushModalAsync() because of console statements and breakpoints. However, the custom rendered page is not displayed and the UI of my app freezes each time after calling PushModalAsync.
Is there something else I need to do to trigger Navigation.PushModalAsync() based on receiving an MQTT notification in the background of my app?
ViewRoomsPage.axml.cs:
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class ViewRoomsPage : ContentPage
{
public ViewRoomsPage()
{
InitializeComponent();
}
public string StreamUri { get; set; }
}
ViewRoomsPage.axml:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="MyForms.Pages.ViewRoomsPage">
<ContentPage.Content>
</ContentPage.Content>
VideoViewerRenderer.cs (video code removed; this should display a blank red screen. It also works when launched from a button on the main screen)
[assembly: ExportRenderer(typeof(ViewRoomsPage), typeof(ViewRoomsRenderer))]
namespace MyForms.IOS.NativeImplementations
{
public class ViewRoomsRenderer : PageRenderer
{
private IJKFFMoviePlayerController _playerController;
protected override void OnElementChanged(VisualElementChangedEventArgs e)
{
base.OnElementChanged(e);
if (e.OldElement != null || Element == null)
{
return;
}
e.NewElement.BackgroundColor = Color.Red;
}
}
}
Method triggered from receiving an MQTT message
public void PushViewRooms()
{
Device.BeginInvokeOnMainThread(async () =>
{
await Application.Current.MainPage.Navigation.PushModalAsync(new ViewRoomsPage());
});
}
In App.xaml.cs:
public partial class App : Application
{
public App()
{
SetupDependencies(); // using StructureMap
Manager = DependencyContainer.Resolve<IMqttManager>();
Manager.Connect();
InitializeComponent();
var mainPage = new MainPage();
MainPage = new NavigationPage(mainPage);
}
}
The problem was a deadlock caused by a Task.WaitAll() being triggered in another section of code running in the background.
Thanks all who helped sanity check that it wasn't something in the way the renderer was set up.

How do you switch between two different Xamarin Form pages?

I'm trying to figure out how to switch between two different pages in Xamarin Forms.
I do not wish to use a NavigationPage (which has that little back arrow that is auto displayed.
I have a Login page (which is a ContentPage) and once the person has authenticated, I then need to navigate to my Dashboard page (which is a TabbedPage).
eg.
Next, one of the Tab's in the TabbedPage is the profile of the logged in user. As such, I need to log them out. So i'll have a button to log them out, which means I will need to navigate them back to the Login page (which was that ContentPage).
I feel like I have two modes the user might be in.
UnAuthorized. (ContentPage)
Authorized. (TabbedPage).
It's like I need to change the App's MainPage to be either one of those two?
To change MainPage to another just do:
App.Current.MainPage = new NavigationPage(new MyContentPage());
or
App.Current.MainPage = new MyContentPage();
BTW: You can use a NavigationPage and then HIDE the toolbar with:
NavigationPage.SetHasNavigationBar(this, false);
You can do navigation as usual by setting MainPage in your Application instance. Small sample.
namespace TestNavigation
{
public class App : Application
{
public App ()
{
// The root page of your application
MainPage = new MyPage1 ();
}
}
}
namespace TestNavigation
{
public class MyPage1 : ContentPage
{
public MyPage1 ()
{
var button = new Button () {
Text = "Click me"
};
button.Clicked += (object sender, EventArgs e) => {
Application.Current.MainPage = new MyPage2 ();
};
Content = new StackLayout {
Children = {
button
}
};
}
}
}
namespace TestNavigation
{
public class MyPage2 : ContentPage
{
public MyPage2 ()
{
Content = new StackLayout {
Children = {
new Label { Text = "Hello ContentPage" }
}
};
}
}
}
EDIT1
I submitted this answer but then I realised that I probably don't understand your problem.
EDIT2
Updated sample.
If you like to switch between the pages then you can simply do
Application.Current.MainPage=new YourPage();
How to Change MainPage in xamarin forms at runtime?
this is what i have done to get result
For a login page, the best practice is to use
await Navigation.PushModalAsync (new LoginPage());
Once you are on the LoginPage, you can use popModelAsync after the validation is passed:
await Navigation.PopModalAsync ();

Resources