¿Cómo se cambia de página en Xamarin.Forms?

99

¿Cómo se cambia entre páginas en Xamarin Forms?

Mi página principal es ContentPage y no quiero cambiar a algo como una página con pestañas.

Pude pseudohacerlo al encontrar los padres de los controles que deberían activar la nueva página hasta que encuentre ContentPage y luego cambie el Contenido con controles para una nueva página. Pero esto parece realmente descuidado.

Eric
fuente
Ya ha habido muchas respuestas a esta pregunta, para ver cómo se puede hacer usando el patrón estructural MVVM, consulte este stackoverflow.com/a/37142513/9403963
Alireza Sattari

Respuestas:

67

Xamarin.Forms admite varios hosts de navegación integrados:

  • NavigationPage, donde se desliza la página siguiente,
  • TabbedPage, el que no te gusta
  • CarouselPage, que permite cambiar de izquierda a derecha a la página siguiente / anterior.

Además de esto, todas las páginas también son compatibles con lo PushModalAsync()que simplemente empuja una nueva página sobre la existente.

Al final, si desea asegurarse de que el usuario no pueda volver a la página anterior (mediante un gesto o el botón de hardware Atrás), puede mantener la misma Pagevisualización y reemplazarla Content.

Las opciones sugeridas para reemplazar la página raíz también funcionan, pero tendrás que manejarlas de manera diferente para cada plataforma.

Stephane Delcroix
fuente
PushModalAsync parece ser parte de Navigation, ¿verdad? No puedo averiguar cómo llegar al objeto / clase de navegación. Supongo que necesito acceder a algo que implemente INavigation, pero ¿qué?
Eric
Si su página está incluida en una NavigationPage, debería poder acceder a la propiedad Navigation desde dentro de su página
Jason
1
Una vez que comencé a usar NavigationPage, todo encajó. Gracias
Eric
1
@stephane, por favor diga si mi primera página es CarouselPage y mi segunda página es masterDetailPage, entonces, ¿cómo puedo cambiar la página? stackoverflow.com/questions/31129845/…
Atul Dhanuka
64

En la clase Aplicación, puede configurar MainPage en una página de navegación y configurar la página raíz en su ContentPage:

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

Luego, en su primera llamada de ContentPage:

Navigation.PushAsync (new SecondContentPage ());
David Douglas
fuente
Hice eso, pero aún así, la página principal es la página predeterminada que se abre. Cualquier página que establezca en la página principal no tiene ningún efecto. Acabo de abrir la primera página que se ha configurado. ¿Cuál es el problema?
Behzad
Visual Studio sugiere importar Android.Content.Respara la navegación. Eso no parece correcto, ¿de dónde tengo que importarlo?
Christian
41

Si su proyecto se ha configurado como un proyecto de formularios PCL (y muy probablemente también como formularios compartidos, pero no lo he probado), hay una clase App.cs que se ve así:

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

puede modificar el GetMainPagemétodo para devolver un nuevo TabbedPaged o alguna otra página que haya definido en el proyecto

A partir de ahí, puede agregar comandos o controladores de eventos para ejecutar código y hacer

// 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
fuente
3
Esto no cambia entre páginas. Esto solo cambia la página que se carga inicialmente.
Dakamojo
tu pregunta se refería a una página principal. vea la respuesta actualizada para ejemplos de navegación
Sten Petrov
¿Qué diablos hay Navigationen este ejemplo? - ¿Es un objeto que creaste en alguna parte? - No lo veo en este ejemplo de código.
BrainSlugs83
La navegación es propiedad en una página
Sten Petrov
Gracias; FTR PushAsync()no funcionó para mí, mientras que PushModalAsync()
tocó el
23

Inserte una nueva página en la pila y luego elimine la página actual. Esto resulta en un cambio.

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

Primero debe estar en una página de navegación:

MainPage = NavigationPage(new FirstPage());

Cambiar contenido no es ideal ya que solo tiene una página grande y un conjunto de eventos de página como OnAppearing, etc.

Daniel Roberts
fuente
Navigation.RemovePage();no es compatible con Android.
Rohit Vipin Mathews
1
Navigation.RemovePage (página); funciona en Android, primero debe estar dentro de una página de navegación.
Daniel Roberts
Lo uso mucho en mi proyecto sobre Forms 1.4.2. Quizás arreglaron el error, o simplemente tuve suerte y aún no lo he encontrado.
Daniel Roberts
Estoy en la última versión y puedo replicarla. Así que creo que tienes mucha suerte.
Rohit Vipin Mathews
2
Sugerencia útil: para eliminar las transiciones al cambiar la página, agregue falso como segundo parámetro:await Navigation.PushAsync(new SecondPage(),false);
Damian Green
8

Si no desea ir a la página anterior, es decir, no permita que el usuario regrese a la pantalla de inicio de sesión una vez que se haya realizado la autorización, puede usar;

 App.Current.MainPage = new HomePage();

Si desea habilitar la funcionalidad de retroceso, simplemente use

Navigation.PushModalAsync(new HomePage())
Baqer Naqvi
fuente
4

Parece que este hilo es muy popular y será triste no mencionar aquí que hay una forma alternativa - ViewModel First Navigation. La mayoría de los frameworks MVVM lo usan, sin embargo, si quieres entender de qué se trata, continúa leyendo.

Toda la documentación oficial de Xamarin.Forms está demostrando una solución simple, aunque un poco no MVVM pura. Esto se debe a que Page(View) no debería saber nada sobre el ViewModely viceversa. Aquí hay un gran ejemplo de esta violación:

// 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 tiene una aplicación de 2 páginas, este enfoque puede ser bueno para usted. Sin embargo, si está trabajando en una gran solución empresarial, será mejor que adopte un ViewModel First Navigationenfoque. Es un enfoque un poco más complicado pero mucho más limpio que le permite navegar entre en ViewModelslugar de navegar entre Pages(Vistas). Una de las ventajas, además de una clara separación de preocupaciones, es que puede pasar fácilmente parámetros al siguienteViewModel o ejecutar un código de inicialización asincrónico justo después de la navegación. Ahora a los detalles.

(Intentaré simplificar todos los ejemplos de código tanto como sea posible).

1. En primer lugar, necesitamos un lugar donde podamos registrar todos nuestros objetos y, opcionalmente, definir su vida útil. Para este asunto podemos usar un contenedor IOC, puede elegir uno usted mismo. En este ejemplo usaré Autofac (es uno de los más rápidos disponibles). Podemos mantener una referencia a él Apppara que esté disponible a nivel mundial (no es una buena idea, pero es necesaria para simplificar):

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. Necesitaremos un objeto responsable de recuperar una Page(Vista) para una específica ViewModely viceversa. El segundo caso puede ser útil en caso de configurar la página raíz / principal de la aplicación. Para eso, deberíamos acordar una convención simple de que todos ViewModelsdeberían estar en el ViewModelsdirectorio y Pages(Vistas) deberían estar en el Viewsdirectorio. En otras palabras, ViewModelsdebería vivir en el [MyApp].ViewModelsespacio de nombres y Pages(Vistas) en el [MyApp].Viewsespacio de nombres. Además de eso, deberíamos estar de acuerdo en que WelcomeView(Página) debería tener un WelcomeViewModely etc. Aquí hay un ejemplo de código de un mapeador:

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.Para el caso de configurar una página raíz, necesitaremos algo ViewModelLocatorque establecerá BindingContextautomáticamente:

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>

Finalmente, necesitaremos un enfoque NavigationServiceque apoye 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);
    }
}

Como puede ver, hay una BaseViewModelclase base abstracta para todos los ViewModelsmétodos donde puede definir métodos InitializeAsyncque se ejecutarán inmediatamente después de la navegación. Y aquí hay un ejemplo de navegación:

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

Como comprenderá, este enfoque es más complicado, más difícil de depurar y puede resultar confuso. Sin embargo, hay muchas ventajas, además de que en realidad no tiene que implementarlo usted mismo, ya que la mayoría de los marcos MVVM lo admiten desde el primer momento. El ejemplo de código que se muestra aquí está disponible en github .

Hay muchos buenos artículos sobre el ViewModel First Navigationenfoque y hay un libro electrónico gratuito de patrones de aplicación empresarial que usa Xamarin.Forms eBook que explica este y muchos otros temas interesantes en detalle.

EvZ
fuente
3

Al usar el método PushAsync (), puede empujar y PopModalAsync () puede abrir páginas hacia y desde la pila de navegación. En mi ejemplo de código a continuación, tengo una página de navegación (página raíz) y desde esta página presiono una página de contenido que es una página de inicio de sesión una vez que he completado mi página de inicio de sesión, vuelvo a la página raíz

~~~ La navegación se puede considerar como una pila de objetos Page de último en entrar, primero en salir. Para pasar de una página a otra, una aplicación colocará una nueva página en esta pila. Para volver a la página anterior, la aplicación sacará la página actual de la pila. Esta navegación en Xamarin.Forms es manejada por la interfaz INavigation

Xamarin.Forms tiene una clase NavigationPage que implementa esta interfaz y administrará la pila de Pages. La clase NavigationPage también agregará una barra de navegación en la parte superior de la pantalla que muestra un título y también tendrá un botón Atrás apropiado para la plataforma que regresará a la página anterior. El siguiente código muestra cómo ajustar una NavigationPage alrededor de la primera página de una aplicación:

Referencia al contenido enumerado anteriormente y un vínculo que debe revisar para obtener más información sobre Xamarin Forms, consulte la sección de navegación:

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

// Se eliminó el código para simplificar, solo se muestra el pop

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

Navegación de una página a otra en Xamarin.forms usando la propiedad Navigation debajo del código de ejemplo

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

Para navegar de una página a otra página con la celda de vista debajo del código Xamrian.forms

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

Ejemplo como el siguiente

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
fuente
2

Llamada:

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

Crea este método dentro de App.xaml.cs:

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

MainPage = new NavigationPage( new YourPage());

Cuando desee navegar desde YourPage a la página siguiente, debe:

await Navigation.PushAsync(new YourSecondPage());

Puede leer más sobre la navegación de Xamarin Forms aquí: https://docs.microsoft.com/en-us/xamarin/xamarin-forms/app-fundamentals/navigation/hierarchical

Microsoft tiene bastante buena documentación sobre esto.

También existe el concepto más nuevo de Shell . Permite una nueva forma de estructurar su aplicación y simplifica la navegación en algunos casos.

Introducción: https://devblogs.microsoft.com/xamarin/shell-xamarin-forms-4-0-getting-started/

Video sobre los conceptos básicos de Shell: https://www.youtube.com/watch?v=0y1bUAcOjZY&t=3112s

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

Jesper Baltzersen
fuente
0

Página XAML agregar esto

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

</ContentPage.ToolbarItems>   

en la página de CS

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

Después de PushAsyncusar PopAsync(con this) para eliminar la página actual.

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

En Xamarin tenemos una página llamada NavigationPage. Tiene pila de ContentPages. NavigationPage tiene métodos como PushAsync () y PopAsync (). PushAsync agrega una página en la parte superior de la pila, en ese momento esa página se convertirá en la página actualmente activa. El método PopAsync () elimina la página de la parte superior de la pila.

En App.Xaml.Cs podemos configurar como.

MainPage = new NavigationPage (nueva YourPage ());

aguardar Navigation.PushAsync (new newPage ()); este método agregará newPage en la parte superior de la pila. En este momento, nePage será la página activa.

PRASAD CP
fuente