I am working on xamarin.form cross-platform application , i want to navigate from one page to another on button click. As i cannot do Navigation.PushAsync(new Page2());
in ViewModel because it only possible in Code-Behid file. please suggest any way to do this?
Here is my View:
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Calculator.Views.SignIn"
xmlns:ViewModels="clr-namespace:Calculator.ViewModels;assembly=Calculator">
<ContentPage.BindingContext>
<ViewModels:LocalAccountViewModel/>
</ContentPage.BindingContext>
<ContentPage.Content>
<StackLayout>
<Button Command="{Binding ContinueBtnClicked}"></Button>
</StackLayout>
</ContentPage.Content>
</ContentPage>
Here is my ViewModel:
public class LocalAccountViewModel : INotifyPropertyChanged
{
public LocalAccountViewModel()
{
this.ContinueBtnClicked = new Command(GotoPage2);
}
public void GotoPage2()
{
/////
}
public ICommand ContinueBtnClicked
{
protected set;
get;
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanges([CallerMemberName] string PropertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(PropertyName));
}
}
One way is you can pass the Navigation through the VM Constructor. Since pages inherit from VisualElement
, they directly inherit the Navigation
property.
Code behind file:
public class SignIn : ContentPage
{
public SignIn(){
InitializeComponent();
// Note the VM constructor takes now a INavigation parameter
BindingContext = new LocalAccountViewModel(Navigation);
}
}
Then in your VM, add a INavigation
property and change the constructor to accept a INavigation
. You can then use this property for navigation:
public class LocalAccountViewModel : INotifyPropertyChanged
{
public INavigation Navigation { get; set;}
public LocalAccountViewModel(INavigation navigation)
{
this.Navigation = navigation;
this.ContinueBtnClicked = new Command(async () => await GotoPage2());
}
public async Task GotoPage2()
{
/////
await Navigation.PushAsync(new Page2());
}
...
Note an issue with your code that you should fix: The GoToPage2()
method must be set async
and return the Task
type. In addition, the command will perform an asynchronous action call. This is because you must do page navigation asychronously!
Hope it helps!
A simple way is
this.ContinueBtnClicked = new Command(async()=>{
await Application.Current.MainPage.Navigation.PushAsync(new Page2());
});
From your VM
public Command RegisterCommand
{
get
{
return new Command(async () =>
{
await Application.Current.MainPage.Navigation.PushAsync(new RegisterNewUser());
});
}
}
I looked into this, and it really depends on how you want to handle your navigation. Do you want your view models to handle your navigation or do you want your views. I found it easiest to have my views handle my navigation so that I could choose to have a different navigation format for different situations or applications. In this situation, rather than using the command binding model, just use a button clicked event and add the new page to the navigation stack in the code behind.
Change your button to something like:
<StackLayout>
<Button Clicked="Button_Clicked"></Button>
</StackLayout>
And in your code behind, implement the method and do the navigation there.
public void Button_Clicked(object sender, EventArgs e)
{
Navigation.PushAsync(new Page2());
}
If you are looking to do viewmodel based navigation, I believe there is a way to do this with MvvmCross, but I am not familiar with that tool.
my approach based on principle every model can navigate to VM context based places of the app only:
In ViewModel i declare INavigationHandler interfeces like that:
public class ItemsViewModel : ViewModelBase
{
public INavigationHandler NavigationHandler { private get; set; }
// some VM code here where in some place i'm invoking
RelayCommand<int> ItemSelectedCommand =>
new RelayCommand<int>((itemID) => { NavigationHandler.NavigateToItemDetail(itemID); });
public interface INavigationHandler
{
void NavigateToItemDetail(int itemID);
}
}
And assign code-behind class as INavigationHandler for ViewModel:
public class ItemsPage : ContentPage, ItemsViewModel.INavigationHandler
{
ItemsViewModel viewModel;
public ItemsPage()
{
viewModel = Container.Default.Get<ItemsViewModel>();
viewModel.NavigationHandler = this;
}
public async void NavigateToItemDetail(int itemID)
{
await Navigation.PushAsync(new ItemDetailPage(itemID));
}
}