web-dev-qa-db-fra.com

Comment changer de page dans Xamarin Forms?

Comment basculer d'une page à l'autre dans Xamarin Forms? Ma page principale est une page de contenu et je ne souhaite pas passer à quelque chose comme une page à onglets.

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

Merci

79
Eric

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

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

En plus de cela, toutes les pages supportent également PushModalAsync() qui pousse simplement une nouvelle page par-dessus celle existante.

En fin de compte, si vous voulez vous assurer que l'utilisateur ne peut pas revenir à la page précédente (à l'aide d'un geste ou du bouton de retour du matériel), vous pouvez laisser la même Page affichée et remplacer sa Content.

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

57
Stephane Delcroix

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 ());
49
David Douglas

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 encore essayé), 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 méthode GetMainPage pour renvoyer un nouveau TabbedPaged ou une autre page que vous avez définie dans le projet

De là, vous pouvez ajouter des commandes ou des gestionnaires d’événements pour exécuter du code et 

// 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();
34
Sten Petrov

Poussez une nouvelle page sur la pile, puis supprimez la page en cours. 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 ne disposez que d'une grande page et d'un ensemble d'événements de page comme OnAppearing ect 

22
Daniel Roberts

Si vous ne voulez pas aller à la page précédente, c’est-à-dire ne laissez pas l’utilisateur revenir à l’écran de connexion une fois l’autorisation donnée, vous pouvez alors utiliser;

 App.Current.MainPage = new HomePage();

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

Navigation.PushModalAsync(new HomePage())
3
Baqer Naqvi

En utilisant la méthode PushAsync (), vous pouvez Push et PopModalAsync () pour extraire des pages de la pile de navigation. Dans mon exemple de code ci-dessous, j'ai une page de navigation (page racine) et à partir de cette page, j'inspire une page de contenu qui est une page de connexion une fois que ma page de connexion est complète.

~~~ Navigation peut être considéré comme une pile d’objets Page de dernier entré, premier sorti. Pour passer d’une page à une autre, une application insère une nouvelle page dans cette pile. Pour revenir à la page précédente, l'application extraira la page actuelle de la pile. Cette navigation dans Xamarin.Forms est gérée par l'interface INavigation

Xamarin.Forms a 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 affichant un titre et un bouton Précédent correspondant à la plateforme, permettant de revenir à la page précédente. Le code suivant montre comment encapsuler une page de navigation 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, voir 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);
    }
}

// Code supprimé pour plus de simplicité, seule la pop est affichée. 

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

Navigation d'une page à une autre dans Xamarin.forms à l'aide de Navigation property Sous 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 à une autre avec la cellule en 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);
        }

    }
2
Manohar

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

Toute la documentation officielle de Xamarin.Forms présente une solution simple, mais légèrement différente, de MVVM. En effet, la Page (View) ne doit rien savoir de la ViewModel et 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 vous convenir. Toutefois, si vous travaillez sur une solution destinée aux grandes entreprises, il vaut mieux utiliser l'approche ViewModel First Navigation. C'est une approche légèrement plus compliquée mais beaucoup plus propre qui vous permet de naviguer entre ViewModels au lieu de naviguer entre Pages (Vues). L'un des avantages de la séparation claire des préoccupations est que vous pouvez facilement passer des paramètres à la prochaine variable ViewModel ou exécuter un code d'initialisation async juste après la navigation. Maintenant aux détails.

(Je vais essayer de simplifier autant que possible tous les exemples de code). 

1. Tout d'abord, nous avons besoin d'un endroit où nous pourrions enregistrer tous nos objets et éventuellement définir leur durée de vie. Pour cela, nous pouvons utiliser un conteneur IOC, vous pouvez en choisir un vous-même. Dans cet exemple, je vais utiliser Autofac (l’un des plus rapides disponibles). Nous pouvons garder une référence à celle-ci dans la App afin qu'elle soit disponible globalement (pas une bonne idée, mais nécessaire pour la simplification):

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 ... */
}

2.Nous aurons besoin d'un objet responsable de la récupération d'une Page (Voir) pour une ViewModel spécifique et vice versa. Le deuxième cas pourrait être utile dans le cas de la définition de la page principale/principale de l'application. Pour cela, nous devrions nous mettre d’accord sur une convention simple voulant que tous les ViewModels soient dans le répertoire ViewModels et que Pages (Views) soit dans le répertoire Views. En d'autres termes, ViewModels devrait résider dans l'espace de noms [MyApp].ViewModels et Pages (Vues) dans l'espace de noms [MyApp].Views. En plus de cela, nous devrions convenir que WelcomeView (Page) devrait avoir WelcomeViewModel et 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);
}

3.Pour le cas de la définition d'une page racine, nous aurons besoin d'une sorte de ViewModelLocator qui définira la BindingContext automatiquement:

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 NavigationService qui supportera l'approche 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 constater, il existe une BaseViewModel - classe de base abstraite pour tous les ViewModels où vous pouvez définir des méthodes telles que InitializeAsync qui seront exécutées immédiatement 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 y a de nombreux avantages et vous n'avez pas à l'implémenter vous-même puisque la plupart des frameworks MVVM le prennent en charge immédiatement. L'exemple de code présenté ici est disponible sur github

Il y a beaucoup de bons articles sur l'approche ViewModel First Navigation et il existe un modèle gratuit Application Application Patterns utilisant Xamarin.Forms eBook, qui explique cela en détail ainsi que de nombreux autres sujets intéressants.

2
EvZ

Appel:

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

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

public void ChangeScreen(Page page)
{
     MainPage = page;
}
1
alansiqueira27

La page XAML ajoute 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());
        }
0
Pxaml
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 dans Xamarin Forms ici: https://docs.Microsoft.com/en-us/xamarin/xamarin-forms/app-fundamentals/navigation/hierarchical

Microsoft a une très bonne documentation à ce sujet.

Il y a aussi le nouveau concept de Shell. Cela 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/

0
Jesper Baltzersen