web-dev-qa-db-fra.com

Navigation de page Xamarin.form dans mvvm

Je travaille sur une application multiplate-forme xamarin.form, je souhaite naviguer d'une page à une autre en cliquant sur un bouton. Comme je ne peux pas faire Navigation.PushAsync(new Page2()); dans ViewModel, cela n’est possible que dans le fichier Code-Behid. s'il vous plaît suggérer un moyen de le faire?

Voici mon point de vue:

<?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>

Voici mon 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));
        }

        }
5
Waleed Arshad

Une solution consiste à passer la navigation via le constructeur VM. Comme les pages héritent de VisualElement, elles héritent directement de la propriété Navigation.

Code derrière le fichier:

public class SignIn : ContentPage
{
    public SignIn(){
       InitializeComponent();
       // Note the VM constructor takes now a INavigation parameter
       BindingContext = new LocalAccountViewModel(Navigation);
    }
}

Ensuite, dans votre machine virtuelle, ajoutez une propriété INavigation et changez le constructeur pour accepter une INavigation. Vous pouvez ensuite utiliser cette propriété pour la 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());
        }


        ...

Notez un problème avec votre code que vous devez corriger: La méthode GoToPage2() doit être définie à async et renvoyer le type Task. En outre, la commande effectuera un appel d'action asynchrone. C'est parce que vous devez faire la navigation de page de manière asynchrone!

J'espère que ça aide!

11
TaiT's

Un moyen simple est

this.ContinueBtnClicked = new Command(async()=>{

    await Application.Current.MainPage.Navigation.PushAsync(new Page2());
});
12

Depuis votre VM

public Command RegisterCommand
        {
            get
            {
                return new Command(async () =>
                {
                    await Application.Current.MainPage.Navigation.PushAsync(new RegisterNewUser());
                });

            }
        }
2
AG70

mon approche repose sur le principe que chaque modèle ne peut naviguer que vers les lieux VM basés sur le contexte de l'application:

Dans ViewModel, je déclare INavigationHandler qui s’interfère comme ceci:

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);
    }
}

Et assignez la classe code-behind comme INavigationHandler pour 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));
    }
}
1
Evgeniy Stepanov

Je me suis penché sur la question et cela dépend vraiment de la manière dont vous voulez gérer votre navigation. Souhaitez-vous que vos modèles de vue gèrent votre navigation ou voulez-vous vos vues? Je trouvais plus facile que mes vues gèrent ma navigation afin que je puisse choisir un format de navigation différent pour différentes situations ou applications. Dans cette situation, plutôt que d'utiliser le modèle de liaison de commande, utilisez simplement un événement sur lequel vous avez cliqué et ajoutez la nouvelle page à la pile de navigation dans le code situé derrière.

Changez votre bouton pour quelque chose comme:

<StackLayout>
    <Button Clicked="Button_Clicked"></Button>
</StackLayout>

Et dans votre code derrière, implémentez la méthode et faites la navigation à cet endroit.

public void Button_Clicked(object sender, EventArgs e)
{
    Navigation.PushAsync(new Page2());
}

Si vous souhaitez effectuer une navigation basée sur un modèle, je pense qu’il existe un moyen de le faire avec MvvmCross, mais je ne connais pas bien cet outil.

1
Rob