Comment changer de page dans Xamarin.Forms?

99

Comment basculer entre les pages dans Xamarin Forms?

Ma page principale est une ContentPage et je ne veux pas passer à quelque chose comme une page à onglets.

J'ai pu le pseudo-faire en trouvant les parents des contrôles qui devraient déclencher la nouvelle page jusqu'à ce que je trouve le ContentPage, puis j'échange le contenu avec des contrôles pour une nouvelle page. Mais cela semble vraiment bâclé.

Eric
la source
Il y a déjà eu beaucoup de réponses à cette question, pour voir comment cela peut être fait en utilisant le modèle structurel MVVM, reportez-vous à ce stackoverflow.com/a/37142513/9403963
Alireza Sattari

Réponses:

67

Xamarin.Forms prend en charge plusieurs hôtes de navigation intégrés:

  • NavigationPage, où la page suivante glisse,
  • TabbedPage, celui que tu n'aimes pas
  • CarouselPage, qui permet de basculer de gauche à droite sur les pages suivantes / précédentes.

En plus de cela, toutes les pages prennent également en charge PushModalAsync()qui ne font que pousser une nouvelle page au-dessus de celle existante.

À la toute fin, si vous voulez vous assurer que l'utilisateur ne peut pas revenir à la page précédente (en utilisant un geste ou le bouton matériel retour), vous pouvez garder le même Pageaffiché et le remplacer Content.

Les options suggérées pour remplacer la page racine fonctionnent également, mais vous devrez gérer cela différemment pour chaque plate-forme.

Stéphane Delcroix
la source
PushModalAsync semble faire partie de la navigation, non? Je ne peux pas comprendre comment accéder à l'objet / à la classe Navigation. Je suppose que j'ai besoin d'accéder à quelque chose qui implémente INavigation, mais quoi?
Eric
Si votre page est contenue dans une NavigationPage, vous devriez pouvoir accéder à la propriété Navigation à partir de votre page
Jason
1
Une fois que j'ai commencé à utiliser NavigationPage, tout s'est mis en place. Merci
Eric
1
@stephane s'il vous plaît dire si ma première page est CarouselPage et ma deuxième page est masterDetailPage alors comment je peux changer la page stackoverflow.com/questions/31129845/...
Atul Dhanuka
64

Dans la classe App, vous pouvez définir MainPage sur une page de navigation et définir la page racine sur votre ContentPage:

public App ()
{
    // The root page of your application
    MainPage = new NavigationPage( new FirstContentPage() );
}

Ensuite, lors de votre premier appel ContentPage:

Navigation.PushAsync (new SecondContentPage ());
David Douglas
la source
Je l'ai fait mais la page principale est toujours la page par défaut qui s'ouvre. Toute page que j'ai définie sur la page principale n'a aucun effet. Je viens d'ouvrir la première page a défini. Quel est le problème?
Behzad
Visual Studio suggère d'importer Android.Content.Respour la navigation. Cela ne semble pas correct, d'où dois-je l'importer?
Christian
41

Si votre projet a été configuré en tant que projet de formulaires PCL (et très probablement également en tant que formulaires partagés, mais je n'ai pas essayé cela), il existe une classe App.cs qui ressemble à ceci:

public class App
{
    public static Page GetMainPage ()
    {     
        AuditorDB.Model.Extensions.AutoTimestamp = true;
        return new NavigationPage (new LoginPage ());
    }
}

vous pouvez modifier la GetMainPageméthode pour renvoyer un nouveau TabbedPaged ou une autre page que vous avez définie dans le projet

À partir de là, vous pouvez ajouter des commandes ou des gestionnaires d'événements pour exécuter du code et faire

// to show OtherPage and be able to go back
Navigation.PushAsync(new OtherPage());

// to show AnotherPage and not have a Back button
Navigation.PushModalAsync(new AnotherPage()); 

// to go back one step on the navigation stack
Navigation.PopAsync();
Sten Petrov
la source
3
Cela ne bascule pas entre les pages. Cela ne change que la page chargée initialement.
dakamojo
votre question parlait d'une page principale. voir la réponse mise à jour pour des exemples de navigation
Sten Petrov
Qu'est-ce que c'est que Navigationcet exemple? - C'est un objet que vous avez créé quelque part? - Je ne le vois pas dans cet exemple de code.
BrainSlugs83
La navigation est propriété sur une page
Sten Petrov
Merci; FTR PushAsync()n'a pas fonctionné pour moi, alors qu'il l'a PushModalAsync()fait
knocte
23

Poussez une nouvelle page sur la pile, puis supprimez la page actuelle. Cela entraîne un changement.

item.Tapped += async (sender, e) => {
    await Navigation.PushAsync (new SecondPage ());
    Navigation.RemovePage(this);
};

Vous devez d'abord être dans une page de navigation:

MainPage = NavigationPage(new FirstPage());

Changer de contenu n'est pas idéal car vous n'avez qu'une seule grande page et un ensemble d'événements de page comme OnAppearing ect.

Daniel Roberts
la source
Navigation.RemovePage();n'est pas pris en charge sur Android.
Rohit Vipin Mathews
1
Navigation.RemovePage (page); fonctionne sous Android, doit d'abord être dans une page de navigation.
Daniel Roberts
Je l'utilise beaucoup dans mon projet sur Forms 1.4.2. Peut-être qu'ils ont corrigé le bogue, ou j'ai juste eu de la chance et je ne l'ai pas encore touché.
Daniel Roberts
Je suis sur la dernière version et je suis capable de la reproduire. Alors je crois que tu as trop de chance.
Rohit Vipin Mathews
2
Astuce pratique - pour supprimer les transitions lors du changement de page, ajoutez false comme deuxième paramètre:await Navigation.PushAsync(new SecondPage(),false);
Damian Green
8

Si vous ne souhaitez pas aller à la page précédente, c'est-à-dire ne pas laisser l'utilisateur revenir à l'écran de connexion une fois l'autorisation effectuée, vous pouvez utiliser;

 App.Current.MainPage = new HomePage();

Si vous souhaitez activer la fonctionnalité de retour, utilisez simplement

Navigation.PushModalAsync(new HomePage())
Baqer Naqvi
la source
4

On dirait que ce fil est très populaire et il sera triste de ne pas mentionner ici qu'il existe un moyen alternatif - ViewModel First Navigation . La plupart des frameworks MVVM qui l'utilisent, mais si vous voulez comprendre de quoi il s'agit, continuez à lire.

Toute la documentation officielle de Xamarin.Forms montre une solution simple, mais légèrement pas pure MVVM. C'est parce que la Page(Vue) ne doit rien savoir sur le ViewModelet vice versa. Voici un excellent exemple de cette violation:

// C# version
public partial class MyPage : ContentPage
{
    public MyPage()
    {
        InitializeComponent();
        // Violation
        this.BindingContext = new MyViewModel();
    }
}

// XAML version
<?xml version="1.0" encoding="utf-8"?>
<ContentPage
    xmlns="http://xamarin.com/schemas/2014/forms"
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
    xmlns:viewmodels="clr-namespace:MyApp.ViewModel"
    x:Class="MyApp.Views.MyPage">
    <ContentPage.BindingContext>
        <!-- Violation -->
        <viewmodels:MyViewModel />
    </ContentPage.BindingContext>
</ContentPage>

Si vous avez une application de 2 pages, cette approche pourrait être bonne pour vous. Cependant, si vous travaillez sur une solution de grande entreprise, vous feriez mieux d'adopter une ViewModel First Navigationapproche. C'est une approche légèrement plus compliquée mais beaucoup plus propre qui vous permet de naviguer ViewModelsentre Pages(Vues) au lieu de naviguer entre (Vues). L'un des avantages à côté de la séparation claire des préoccupations est que vous pouvez facilement passer des paramètres au suivant.ViewModel ou exécuter un code d'initialisation asynchrone juste après la navigation. Passons maintenant aux détails.

(Je vais essayer de simplifier au maximum tous les exemples de code).

1. Tout d'abord, nous avons besoin d'un endroit où nous pourrions enregistrer tous nos objets et définir éventuellement leur durée de vie. Pour cela, nous pouvons utiliser un conteneur IOC, vous pouvez en choisir un vous-même. Dans cet exemple, j'utiliserai Autofac (c'est l'un des plus rapides disponibles). Nous pouvons en garder une référence dans le Appafin qu'il soit disponible dans le monde entier (ce n'est pas une bonne idée, mais nécessaire pour simplifier):

public class DependencyResolver
{
    static IContainer container;

    public DependencyResolver(params Module[] modules)
    {
        var builder = new ContainerBuilder();

        if (modules != null)
            foreach (var module in modules)
                builder.RegisterModule(module);

        container = builder.Build();
    }

    public T Resolve<T>() => container.Resolve<T>();
    public object Resolve(Type type) => container.Resolve(type);
}

public partial class App : Application
{
    public DependencyResolver DependencyResolver { get; }

    // Pass here platform specific dependencies
    public App(Module platformIocModule)
    {
        InitializeComponent();
        DependencyResolver = new DependencyResolver(platformIocModule, new IocModule());
        MainPage = new WelcomeView();
    }

    /* The rest of the code ... */
}

Nous aurons besoin d'un objet chargé de récupérer une Page(Vue) pour un objet spécifique ViewModelet vice versa. Le deuxième cas peut être utile en cas de configuration de la page racine / principale de l'application. Pour cela, nous devrions nous mettre d'accord sur une convention simple selon laquelle tous les ViewModelsdevraient être dans le ViewModelsrépertoire et Pages(Views) devraient être dans le Viewsrépertoire. En d'autres termes, ViewModelsdevrait vivre dans l' [MyApp].ViewModelsespace de noms et Pages(Vues) dans l' [MyApp].Viewsespace de noms. En plus de cela, nous devrions convenir que WelcomeView(Page) devrait avoir un WelcomeViewModelet etc. Voici un exemple de code d'un mappeur:

public class TypeMapperService
{
    public Type MapViewModelToView(Type viewModelType)
    {
        var viewName = viewModelType.FullName.Replace("Model", string.Empty);
        var viewAssemblyName = GetTypeAssemblyName(viewModelType);
        var viewTypeName = GenerateTypeName("{0}, {1}", viewName, viewAssemblyName);
        return Type.GetType(viewTypeName);
    }

    public Type MapViewToViewModel(Type viewType)
    {
        var viewModelName = viewType.FullName.Replace(".Views.", ".ViewModels.");
        var viewModelAssemblyName = GetTypeAssemblyName(viewType);
        var viewTypeModelName = GenerateTypeName("{0}Model, {1}", viewModelName, viewModelAssemblyName);
        return Type.GetType(viewTypeModelName);
    }

    string GetTypeAssemblyName(Type type) => type.GetTypeInfo().Assembly.FullName;
    string GenerateTypeName(string format, string typeName, string assemblyName) =>
        string.Format(CultureInfo.InvariantCulture, format, typeName, assemblyName);
}

Dans le cas de la configuration d'une page racine, nous aurons besoin d'une sorte de ViewModelLocatorparamètre qui définira BindingContextautomatiquement:

public static class ViewModelLocator
{
    public static readonly BindableProperty AutoWireViewModelProperty =
        BindableProperty.CreateAttached("AutoWireViewModel", typeof(bool), typeof(ViewModelLocator), default(bool), propertyChanged: OnAutoWireViewModelChanged);

    public static bool GetAutoWireViewModel(BindableObject bindable) =>
        (bool)bindable.GetValue(AutoWireViewModelProperty);

    public static void SetAutoWireViewModel(BindableObject bindable, bool value) =>
        bindable.SetValue(AutoWireViewModelProperty, value);

    static ITypeMapperService mapper = (Application.Current as App).DependencyResolver.Resolve<ITypeMapperService>();

    static void OnAutoWireViewModelChanged(BindableObject bindable, object oldValue, object newValue)
    {
        var view = bindable as Element;
        var viewType = view.GetType();
        var viewModelType = mapper.MapViewToViewModel(viewType);
        var viewModel =  (Application.Current as App).DependencyResolver.Resolve(viewModelType);
        view.BindingContext = viewModel;
    }
}

// Usage example
<?xml version="1.0" encoding="utf-8"?>
<ContentPage
    xmlns="http://xamarin.com/schemas/2014/forms"
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
    xmlns:viewmodels="clr-namespace:MyApp.ViewModel"
    viewmodels:ViewModelLocator.AutoWireViewModel="true"
    x:Class="MyApp.Views.MyPage">
</ContentPage>

4.Enfin, nous aurons besoin d'une approche NavigationServicequi soutiendra ViewModel First Navigation:

public class NavigationService
{
    TypeMapperService mapperService { get; }

    public NavigationService(TypeMapperService mapperService)
    {
        this.mapperService = mapperService;
    }

    protected Page CreatePage(Type viewModelType)
    {
        Type pageType = mapperService.MapViewModelToView(viewModelType);
        if (pageType == null)
        {
            throw new Exception($"Cannot locate page type for {viewModelType}");
        }

        return Activator.CreateInstance(pageType) as Page;
    }

    protected Page GetCurrentPage()
    {
        var mainPage = Application.Current.MainPage;

        if (mainPage is MasterDetailPage)
        {
            return ((MasterDetailPage)mainPage).Detail;
        }

        // TabbedPage : MultiPage<Page>
        // CarouselPage : MultiPage<ContentPage>
        if (mainPage is TabbedPage || mainPage is CarouselPage)
        {
            return ((MultiPage<Page>)mainPage).CurrentPage;
        }

        return mainPage;
    }

    public Task PushAsync(Page page, bool animated = true)
    {
        var navigationPage = Application.Current.MainPage as NavigationPage;
        return navigationPage.PushAsync(page, animated);
    }

    public Task PopAsync(bool animated = true)
    {
        var mainPage = Application.Current.MainPage as NavigationPage;
        return mainPage.Navigation.PopAsync(animated);
    }

    public Task PushModalAsync<TViewModel>(object parameter = null, bool animated = true) where TViewModel : BaseViewModel =>
        InternalPushModalAsync(typeof(TViewModel), animated, parameter);

    public Task PopModalAsync(bool animated = true)
    {
        var mainPage = GetCurrentPage();
        if (mainPage != null)
            return mainPage.Navigation.PopModalAsync(animated);

        throw new Exception("Current page is null.");
    }

    async Task InternalPushModalAsync(Type viewModelType, bool animated, object parameter)
    {
        var page = CreatePage(viewModelType);
        var currentNavigationPage = GetCurrentPage();

        if (currentNavigationPage != null)
        {
            await currentNavigationPage.Navigation.PushModalAsync(page, animated);
        }
        else
        {
            throw new Exception("Current page is null.");
        }

        await (page.BindingContext as BaseViewModel).InitializeAsync(parameter);
    }
}

Comme vous pouvez le voir, il existe une BaseViewModelclasse de base abstraite pour tous les ViewModelsdomaines dans lesquels vous pouvez définir des méthodes comme InitializeAsynccelle-ci qui seront exécutées juste après la navigation. Et voici un exemple de navigation:

public class WelcomeViewModel : BaseViewModel
{
    public ICommand NewGameCmd { get; }
    public ICommand TopScoreCmd { get; }
    public ICommand AboutCmd { get; }

    public WelcomeViewModel(INavigationService navigation) : base(navigation)
    {
        NewGameCmd = new Command(async () => await Navigation.PushModalAsync<GameViewModel>());
        TopScoreCmd = new Command(async () => await navigation.PushModalAsync<TopScoreViewModel>());
        AboutCmd = new Command(async () => await navigation.PushModalAsync<AboutViewModel>());
    }
}

Comme vous le comprenez, cette approche est plus compliquée, plus difficile à déboguer et peut prêter à confusion. Cependant, il existe de nombreux avantages et vous n'avez pas à l'implémenter vous-même puisque la plupart des frameworks MVVM le prennent en charge dès la sortie de la boîte. L'exemple de code présenté ici est disponible sur github .

Il y a beaucoup de bons articles sur l' ViewModel First Navigationapproche et il existe un eBook gratuit pour les applications d'entreprise utilisant Xamarin.Forms eBook qui explique cela et de nombreux autres sujets intéressants en détail.

EvZ
la source
3

En utilisant la méthode PushAsync (), vous pouvez pousser et PopModalAsync () vous pouvez afficher des pages vers et depuis la pile de navigation. Dans mon exemple de code ci-dessous, j'ai une page de navigation (page racine) et à partir de cette page, je pousse une page de contenu qui est une page de connexion une fois que je suis complet avec ma page de connexion, je reviens à la page racine

~~~ La navigation peut être considérée comme une pile d'objets de page dernier entré, premier sorti. Pour passer d'une page à une autre, une application poussera une nouvelle page sur cette pile. Pour revenir à la page précédente, l'application fera apparaître la page actuelle de la pile. Cette navigation dans Xamarin.Forms est gérée par l'interface INavigation

Xamarin.Forms possède une classe NavigationPage qui implémente cette interface et gérera la pile de Pages. La classe NavigationPage ajoutera également une barre de navigation en haut de l'écran qui affiche un titre et aura également un bouton Retour approprié à la plate-forme qui reviendra à la page précédente. Le code suivant montre comment encapsuler une NavigationPage autour de la première page d'une application:

Référence au contenu répertorié ci-dessus et un lien que vous devez consulter pour plus d'informations sur Xamarin Forms, consultez la section Navigation:

http://developer.xamarin.com/guides/cross-platform/xamarin-forms/introduction-to-xamarin-forms/

~~~

public class MainActivity : AndroidActivity
{
    protected override void OnCreate(Bundle bundle)
    {
        base.OnCreate(bundle);

        Xamarin.Forms.Forms.Init(this, bundle);
        // Set our view from the "main" layout resource
        SetPage(BuildView());
    }

    static Page BuildView()
    {
        var mainNav = new NavigationPage(new RootPage());
        return mainNav;
    }
}


public class RootPage : ContentPage
{
    async void ShowLoginDialog()
    {
        var page = new LoginPage();

        await Navigation.PushModalAsync(page);
    }
}

// Suppression du code pour plus de simplicité, seul le pop est affiché

private async void AuthenticationResult(bool isValid)
{
    await navigation.PopModalAsync();
}
Chad Bonthuys
la source
2

Navigation d'une page à une autre dans Xamarin.forms à l'aide de la propriété Navigation sous l'exemple de code

void addClicked(object sender, EventArgs e)
        {
            //var createEmp = (Employee)BindingContext;
            Employee emp = new Employee();
            emp.Address = AddressEntry.Text;   
            App.Database.SaveItem(emp);
            this.Navigation.PushAsync(new EmployeeDetails());
  this.Navigation.PushModalAsync(new EmployeeDetails());
        }

Pour naviguer d'une page vers une autre page avec la cellule de vue Ci-dessous le code Xamrian.forms

 private async void BtnEdit_Clicked1(object sender, EventArgs e)
        {
            App.Database.GetItem(empid);
            await App.Current.MainPage.Navigation.PushModalAsync(new EmployeeRegistration(empid));
        }

Exemple comme ci-dessous

public class OptionsViewCell : ViewCell
    {
        int empid;
        Button btnEdit;
        public OptionsViewCell()
        {
        }
        protected override void OnBindingContextChanged()
        {
            base.OnBindingContextChanged();

            if (this.BindingContext == null)
                return;

            dynamic obj = BindingContext;
            empid = Convert.ToInt32(obj.Eid);
            var lblname = new Label
            {
                BackgroundColor = Color.Lime,
                Text = obj.Ename,
            };

            var lblAddress = new Label
            {
                BackgroundColor = Color.Yellow,
                Text = obj.Address,
            };

            var lblphonenumber = new Label
            {
                BackgroundColor = Color.Pink,
                Text = obj.phonenumber,
            };

            var lblemail = new Label
            {
                BackgroundColor = Color.Purple,
                Text = obj.email,
            };

            var lbleid = new Label
            {
                BackgroundColor = Color.Silver,
                Text = (empid).ToString(),
            };

             //var lbleid = new Label
            //{
            //    BackgroundColor = Color.Silver,
            //    // HorizontalOptions = LayoutOptions.CenterAndExpand
            //};
            //lbleid.SetBinding(Label.TextProperty, "Eid");
            Button btnDelete = new Button
            {
                BackgroundColor = Color.Gray,

                Text = "Delete",
                //WidthRequest = 15,
                //HeightRequest = 20,
                TextColor = Color.Red,
                HorizontalOptions = LayoutOptions.EndAndExpand,
            };
            btnDelete.Clicked += BtnDelete_Clicked;
            //btnDelete.PropertyChanged += BtnDelete_PropertyChanged;  

            btnEdit = new Button
            {
                BackgroundColor = Color.Gray,
                Text = "Edit",
                TextColor = Color.Green,
            };
            // lbleid.SetBinding(Label.TextProperty, "Eid");
            btnEdit.Clicked += BtnEdit_Clicked1; ;
            //btnEdit.Clicked += async (s, e) =>{
            //    await App.Current.MainPage.Navigation.PushModalAsync(new EmployeeRegistration());
            //};

            View = new StackLayout()
            {
                Orientation = StackOrientation.Horizontal,
                BackgroundColor = Color.White,
                Children = { lbleid, lblname, lblAddress, lblemail, lblphonenumber, btnDelete, btnEdit },
            };

        }

        private async void BtnEdit_Clicked1(object sender, EventArgs e)
        {
            App.Database.GetItem(empid);
            await App.Current.MainPage.Navigation.PushModalAsync(new EmployeeRegistration(empid));
        }



        private void BtnDelete_Clicked(object sender, EventArgs e)
        {
            // var eid = Convert.ToInt32(empid);
            // var item = (Xamarin.Forms.Button)sender;
            int eid = empid;
            App.Database.DeleteItem(empid);
        }

    }
Manohar
la source
2

Appel:

((App)App.Current).ChangeScreen(new Map());

Créez cette méthode dans App.xaml.cs:

public void ChangeScreen(Page page)
{
     MainPage = page;
}
alansiqueira27
la source
2
In App.Xaml.Cs:

MainPage = new NavigationPage( new YourPage());

Lorsque vous souhaitez naviguer de YourPage à la page suivante, vous faites:

await Navigation.PushAsync(new YourSecondPage());

Vous pouvez en savoir plus sur la navigation Xamarin Forms ici: https://docs.microsoft.com/en-us/xamarin/xamarin-forms/app-fundamentals/navigation/hierarchical

Microsoft a d'assez bons documents à ce sujet.

Il y a aussi le nouveau concept du Shell. Il permet une nouvelle façon de structurer votre application et simplifie la navigation dans certains cas.

Intro: https://devblogs.microsoft.com/xamarin/shell-xamarin-forms-4-0-getting-started/

Vidéo sur les bases de Shell: https://www.youtube.com/watch?v=0y1bUAcOjZY&t=3112s

Docs: https://docs.microsoft.com/en-us/xamarin/xamarin-forms/app-fundamentals/shell/

Jesper Baltzersen
la source
0

Page XAML ajouter ceci

<ContentPage.ToolbarItems>
            <ToolbarItem Text="Next" Order="Primary"
            Activated="Handle_Activated"/>

</ContentPage.ToolbarItems>   

sur la page CS

 async void Handle_Activated(object sender, System.EventArgs e)
        {
            await App.Navigator.PushAsync(new PAGE());
        }
Pxaml
la source
0

Après PushAsyncutilisation PopAsync(avec this) pour supprimer la page actuelle.

await Navigation.PushAsync(new YourSecondPage());
this.Navigation.PopAsync(this);
AliSafder
la source
0

Dans Xamarin, nous avons une page appelée NavigationPage. Il contient une pile de ContentPages. NavigationPage a une méthode comme PushAsync () et PopAsync (). PushAsync ajoute une page en haut de la pile, à ce moment-là, cette page deviendra la page actuellement active. La méthode PopAsync () supprime la page du haut de la pile.

Dans App.Xaml.Cs, nous pouvons définir comme.

MainPage = new NavigationPage (new YourPage ());

attendre Navigation.PushAsync (new newPage ()); cette méthode ajoutera newPage en haut de la pile. À ce moment, nePage sera la page actuellement active.

PRASAD CP
la source