Come si cambia pagina in Xamarin.Forms?


99

Come si passa da una pagina all'altra in Xamarin Forms?

La mia pagina principale è una ContentPage e non voglio passare a qualcosa come una pagina a schede.

Sono stato in grado di pseudo-farlo trovando i genitori dei controlli che dovrebbero attivare la nuova pagina fino a trovare ContentPage e quindi scambiare il contenuto con i controlli per una nuova pagina. Ma questo sembra davvero sciatto.


Ci sono già state molte risposte a questa domanda, per vedere come può essere fatto utilizzando il modello strutturale MVVM, fare riferimento a questo stackoverflow.com/a/37142513/9403963
Alireza Sattari

Risposte:


67

Xamarin.Forms supporta più host di navigazione integrati:

  • NavigationPage, dove scorre la pagina successiva,
  • TabbedPage, quello che non ti piace
  • CarouselPage, che consente di passare da sinistra a destra alle pagine successiva / precedente.

Inoltre, supportano anche tutte le pagine PushModalAsync() semplice inserimento di una nuova pagina sopra quella esistente.

Alla fine, se vuoi assicurarti che l'utente non possa tornare alla pagina precedente (usando un gesto o il pulsante hardware indietro), puoi mantenere lo stesso Page visualizzato e sostituirloContent .

Anche le opzioni suggerite per sostituire la pagina principale funzionano, ma dovrai gestirle in modo diverso per ciascuna piattaforma.


PushModalAsync sembra far parte della navigazione, giusto? Non riesco a capire come arrivare all'oggetto / classe di navigazione. Presumo di aver bisogno di accedere a qualcosa che implementa INavigation, ma cosa?
Eric

Se la tua pagina è contenuta in una NavigationPage dovresti essere in grado di accedere alla proprietà Navigation dall'interno della tua pagina
Jason

1
Una volta che ho iniziato a utilizzare NavigationPage, tutto è andato a posto. Grazie
Eric

1
@stephane informi se la mia prima pagina è CarouselPage e la mia seconda pagina è masterDetailPage allora come è possibile passare alla pagina stackoverflow.com/questions/31129845/...
Atul Dhanuka

64

Nella classe App puoi impostare la MainPage su una pagina di navigazione e impostare la pagina principale sulla tua ContentPage:

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

Quindi nella tua prima chiamata a ContentPage:

Navigation.PushAsync (new SecondContentPage ());

L'ho fatto ma la pagina principale è ancora la pagina predefinita che si apre. Qualsiasi pagina che ho impostato come pagina principale non ha alcun effetto. Ho appena aperto la prima pagina impostata. Qual è il problema?
Behzad

Visual Studio suggerisce l'importazione Android.Content.Resper la navigazione. Non sembra corretto, da dove devo importarlo?
Christian

41

Se il tuo progetto è stato impostato come un progetto di moduli PCL (e molto probabilmente anche come moduli condivisi, ma non l'ho provato) c'è una classe App.cs simile a questa:

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

puoi modificare il file GetMainPage metodo per restituire una nuova TabbedPaged o un'altra pagina che hai definito nel progetto

Da lì in poi puoi aggiungere comandi o gestori di eventi per eseguire codice e fare

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

3
Questo non cambia tra le pagine. Questo cambia solo quale pagina viene caricata inizialmente.
dakamojo

la tua domanda stava parlando di una pagina principale. vedi risposta aggiornata per esempi di navigazione
Sten Petrov

Che diavolo c'è Navigationin questo esempio? - È un oggetto che hai creato da qualche parte? - Non lo vedo in questo esempio di codice.
BrainSlugs83

La navigazione è di proprietà su una pagina
Sten Petrov

Grazie; FTR PushAsync()non ha funzionato per me, mentre lo ha PushModalAsync()fatto
knocte

23

Metti una nuova pagina nella pila, quindi rimuovi la pagina corrente. Ciò si traduce in un interruttore.

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

Devi prima trovarti in una pagina di navigazione:

MainPage = NavigationPage(new FirstPage());

Cambiare contenuto non è l'ideale in quanto hai solo una grande pagina e un insieme di eventi di pagina come OnAppearing ect.


Navigation.RemovePage();non è supportato su Android.
Rohit Vipin Mathews

1
Navigation.RemovePage (pagina); funziona in Android, deve essere prima all'interno di una pagina di navigazione.
Daniel Roberts,

Lo uso ampiamente nel mio progetto su Forms 1.4.2. Forse hanno risolto il bug, o sono stato fortunato e non l'ho ancora raggiunto.
Daniel Roberts

Sono sull'ultima versione e sono in grado di replicarla. Quindi credo che tu sia troppo fortunato.
Rohit Vipin Mathews

2
Suggerimento pratico: per rimuovere le transizioni quando si cambia pagina, aggiungere false come secondo parametro:await Navigation.PushAsync(new SecondPage(),false);
Damian Green

8

Se non vuoi andare alla pagina precedente, cioè non lasciare che l'utente torni alla schermata di login una volta che l'autorizzazione è stata fatta, puoi usare;

 App.Current.MainPage = new HomePage();

Se vuoi abilitare la funzionalità Indietro, usa

Navigation.PushModalAsync(new HomePage())

4

Sembra che questo thread sia molto popolare e sarà triste non menzionare qui che esiste un modo alternativo - ViewModel First Navigation . La maggior parte dei framework MVVM disponibili lo utilizzano, tuttavia se vuoi capire di cosa si tratta, continua a leggere.

Tutta la documentazione ufficiale di Xamarin.Forms mostra una soluzione semplice, ma leggermente non MVVM. Questo perché Page(View) non dovrebbe sapere nulla di ViewModele viceversa. Ecco un ottimo esempio di questa violazione:

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

Se hai un'applicazione di 2 pagine, questo approccio potrebbe essere utile. Tuttavia, se stai lavorando a una soluzione per grandi aziende, è meglio che tu scelga un fileViewModel First Navigation approccio. È un approccio leggermente più complicato ma molto più pulito che ti consente di navigare tra ViewModelsle Pages(Visualizzazioni) invece di navigare . Uno dei vantaggi, oltre alla chiara separazione delle preoccupazioni, è che potresti facilmente passare i parametri al successivoViewModel o eseguire un codice di inizializzazione asincrono subito dopo la navigazione. Ora ai dettagli.

(Cercherò di semplificare il più possibile tutti gli esempi di codice).

1. Prima di tutto abbiamo bisogno di un luogo in cui registrare tutti i nostri oggetti e opzionalmente definire la loro durata. Per questo possiamo usare un contenitore IOC, puoi sceglierne uno tu stesso. In questo esempio userò Autofac (è uno dei più veloci disponibili). Possiamo mantenere un riferimento ad esso in Appmodo che sia disponibile a livello globale (non una buona idea, ma necessaria per semplificazione):

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.Avremo bisogno di un oggetto responsabile del recupero di una Page(Vista) per uno specifico ViewModele viceversa. Il secondo caso potrebbe essere utile in caso di impostazione della pagina principale / principale dell'app. Per questo dovremmo concordare una semplice convenzione secondo cui tutto ViewModelsdovrebbe essere nella ViewModelsdirectory e Pages(Views) dovrebbe essere nella Viewsdirectory. In altre parole, ViewModelsdovrebbe risiedere nello [MyApp].ViewModelsspazio dei nomi e Pages(Visualizzazioni) nello [MyApp].Viewsspazio dei nomi. In aggiunta a ciò dovremmo convenire che WelcomeView(Pagina) dovrebbe avere un fileWelcomeViewModel ed ecc. Ecco un esempio di codice di un mappatore:

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.Per il caso di impostazione di una pagina principale, avremo bisogno di un tipo ViewModelLocatorche imposterà l'estensioneBindingContext automaticamente:

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.Infine avremo bisogno di un NavigationServicesupporto che supportiViewModel First Navigation approccio :

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

Come puoi vedere, esiste una BaseViewModelclasse base astratta per tutti i casi in ViewModelscui puoi definire metodi InitializeAsyncche verranno eseguiti subito dopo la navigazione. Ed ecco un esempio di navigazione:

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

Come capisci, questo approccio è più complicato, più difficile da eseguire il debug e potrebbe creare confusione. Tuttavia ci sono molti vantaggi e in realtà non è necessario implementarlo da soli poiché la maggior parte dei framework MVVM lo supporta immediatamente. L'esempio di codice mostrato qui è disponibile su GitHub .

Ci sono molti buoni articoli ViewModel First Navigationsull'approccio e c'è un Enterprise Application Patterns gratuito che usa Xamarin.Forms eBook che spiega questo e molti altri argomenti interessanti in dettaglio.


3

Utilizzando il metodo PushAsync () è possibile eseguire il push e PopModalAsync () è possibile visualizzare le pagine da e verso lo stack di navigazione. Nel mio esempio di codice qui sotto ho una pagina di navigazione (pagina principale) e da questa pagina spingo una pagina di contenuto che è una pagina di accesso una volta completata la mia pagina di accesso torno alla pagina principale

~~~ La navigazione può essere pensata come una pila di oggetti Page. Per spostarsi da una pagina all'altra, un'applicazione inserirà una nuova pagina in questa pila. Per tornare alla pagina precedente, l'applicazione aprirà la pagina corrente dallo stack. Questa navigazione in Xamarin.Forms viene gestita dall'interfaccia INavigation

Xamarin.Forms ha una classe NavigationPage che implementa questa interfaccia e gestirà lo stack di Pages. La classe NavigationPage aggiungerà anche una barra di navigazione nella parte superiore dello schermo che visualizza un titolo e avrà anche un pulsante Indietro appropriato per la piattaforma che tornerà alla pagina precedente. Il codice seguente mostra come avvolgere una NavigationPage attorno alla prima pagina in un'applicazione:

Riferimento al contenuto elencato sopra e un collegamento da esaminare per ulteriori informazioni sui moduli Xamarin, vedere la sezione Navigazione:

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

// Rimosso il codice per semplicità viene visualizzato solo il pop

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

2

Navigazione da una pagina all'altra in Xamarin.forms usando la proprietà di navigazione Sotto il codice di esempio

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

Per passare da una pagina all'altra con la cella in vista Sotto il codice Xamrian.forms

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

Esempio come sotto

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

Chiamata:

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

Crea questo metodo all'interno di App.xaml.cs:

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

2
In App.Xaml.Cs:

MainPage = new NavigationPage( new YourPage());

Quando desideri navigare da YourPage alla pagina successiva:

await Navigation.PushAsync(new YourSecondPage());

Puoi leggere ulteriori informazioni sulla navigazione in Xamarin Forms qui: https://docs.microsoft.com/en-us/xamarin/xamarin-forms/app-fundamentals/navigation/hierarchical

Microsoft ha documenti abbastanza buoni su questo.

C'è anche il nuovo concetto di Shell . Consente un nuovo modo di strutturare l'applicazione e in alcuni casi semplifica la navigazione.

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

Video sulle basi di Shell: https://www.youtube.com/watch?v=0y1bUAcOjZY&t=3112s

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


0

Aggiungi questo alla pagina XAML

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

</ContentPage.ToolbarItems>   

nella pagina CS

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

0

Dopo l' PushAsyncuso PopAsync(con this) per rimuovere la pagina corrente.

await Navigation.PushAsync(new YourSecondPage());
this.Navigation.PopAsync(this);

0

In Xamarin abbiamo una pagina chiamata NavigationPage. Contiene una pila di ContentPages. NavigationPage ha metodi come PushAsync () e PopAsync (). PushAsync aggiunge una pagina in cima allo stack, in quel momento quella pagina diventerà la pagina attualmente attiva. Il metodo PopAsync () rimuove la pagina dalla cima dello stack.

In App.Xaml.Cs possiamo impostare come.

MainPage = new NavigationPage (new YourPage ());

attende Navigation.PushAsync (new newPage ()); questo metodo aggiungerà newPage all'inizio dello stack. In questo momento nePage sarà la pagina attualmente attiva.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.