Como você muda de página em Xamarin.Forms?

99

Como você alterna entre as páginas no Formulários Xamarin?

Minha página principal é uma ContentPage e não quero mudar para algo como uma página com guias.

Consegui pseudo-fazer, encontrando os pais dos controles que deveriam acionar a nova página até encontrar a ContentPage e, em seguida, trocar o Conteúdo com controles por uma nova página. Mas isso parece muito desleixado.

Eric
fonte
Já houve muitas respostas para essa pergunta, para ver como isso pode ser feito usando o padrão estrutural MVVM, consulte este stackoverflow.com/a/37142513/9403963
Alireza Sattari

Respostas:

67

Xamarin.Forms suporta vários hosts de navegação integrados:

  • NavigationPage, onde a próxima página desliza,
  • TabbedPage, o que você não gosta
  • CarouselPage, que permite alternar para a esquerda e para a direita para as páginas seguintes / anteriores.

Além disso, todas as páginas também suportam o PushModalAsync()que apenas enviar uma nova página sobre a existente.

No final, se você quiser ter certeza de que o usuário não pode voltar à página anterior (usando um gesto ou o botão de voltar do hardware), você pode manter o mesmo Page exibida e substituí-la Content.

As opções sugeridas para substituir a página raiz também funcionam, mas você terá que lidar com isso de forma diferente para cada plataforma.

Stephane Delcroix
fonte
PushModalAsync parece fazer parte da navegação, certo? Não consigo descobrir como chegar ao objeto / classe Navigation. Presumo que preciso acessar algo que implemente INavigation, mas o quê?
Eric
Se a sua página estiver contida em uma NavigationPage, você deverá conseguir acessar a propriedade Navigation de dentro da sua página
Jason
1
Depois que comecei a usar NavigationPage, tudo se encaixou. Obrigado
Eric
1
@stephane, diga se minha primeira página é CarouselPage e minha segunda página é masterDetailPage, então como posso mudar a página stackoverflow.com/questions/31129845/…
Atul Dhanuka 01 de
64

Na classe App, você pode definir a MainPage como uma página de navegação e definir a página raiz como sua ContentPage:

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

Então, em sua primeira chamada ContentPage:

Navigation.PushAsync (new SecondContentPage ());
David Douglas
fonte
Eu fiz isso, mas a página principal ainda é a página padrão que se abre. Qualquer página que eu definir para a página principal não tem efeito. Acabei de abrir a primeira página definida. Qual é o problema?
Behzad de
O Visual Studio sugere a importação Android.Content.Respara navegação. Isso não parece estar certo, de onde devo importá-lo?
Cristão
41

Se o seu projeto foi configurado como um projeto de formulários PCL (e muito provavelmente também como formulários compartilhados, mas eu não tentei isso), há uma classe App.cs que se parece com isto:

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

você pode modificar o GetMainPagemétodo para retornar uma nova TabbedPaged ou alguma outra página que você definiu no projeto

A partir daí, você pode adicionar comandos ou manipuladores de eventos para executar o código e fazer

// 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
fonte
3
Isso não alterna entre as páginas. Isso muda apenas a página que é carregada inicialmente.
Dakamojo
sua pergunta estava falando sobre uma página principal. veja a resposta atualizada para exemplos de navegação
Sten Petrov
O que diabos está Navigationneste exemplo? - É um objeto que você criou em algum lugar? - Não estou vendo neste exemplo de código.
BrainSlugs83
A navegação é propriedade em uma página
Sten Petrov
obrigado; FTR PushAsync()não funcionou para mim, mas PushModalAsync()sim
knocte
23

Empurre uma nova página na pilha e remova a página atual. Isso resulta em uma troca.

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

Você precisa estar em uma página de navegação primeiro:

MainPage = NavigationPage(new FirstPage());

Alternar conteúdo não é o ideal, pois você tem apenas uma grande página e um conjunto de eventos de página como OnAppearing ect.

Daniel Roberts
fonte
Navigation.RemovePage();não é compatível com Android.
Rohit Vipin Mathews
1
Navigation.RemovePage (página); funciona no Android, precisa estar dentro de uma página de navegação primeiro.
Daniel Roberts
Eu o uso extensivamente em meu projeto no Forms 1.4.2. Talvez eles tenham corrigido o bug, ou eu apenas tive sorte e ainda não o acertei.
Daniel Roberts
Estou com a versão mais recente e posso replicá-la. Então eu acredito que você tem muita sorte.
Rohit Vipin Mathews
2
Dica útil - para remover as transições ao mudar a página, adicione falso como o segundo parâmetro:await Navigation.PushAsync(new SecondPage(),false);
Damian Green
8

Se você não quiser ir para a página anterior, ou seja, não permitir que o usuário volte para a tela de login depois que a autorização for concluída, você pode usar;

 App.Current.MainPage = new HomePage();

Se você quiser habilitar a funcionalidade de volta, basta usar

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

Parece que este tópico é muito popular e será triste não mencionar aqui que existe uma forma alternativa - ViewModel First Navigation. A maioria dos frameworks MVVM que o utilizam, no entanto, se você quiser entender do que se trata, continue lendo.

Toda a documentação oficial do Xamarin.Forms está demonstrando uma solução simples, mas um pouco diferente do MVVM puro. Isso ocorre porque o Page(View) não deve saber nada sobre o ViewModele vice-versa. Aqui está um ótimo exemplo dessa violação:

// 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 você tem um aplicativo de 2 páginas, essa abordagem pode ser boa para você. No entanto, se você estiver trabalhando em uma solução para uma grande empresa, é melhor escolher uma ViewModel First Navigationabordagem. É uma abordagem um pouco mais complicada, mas muito mais limpa, que permite navegar entre em ViewModelsvez de navegar entre Pages(visualizações). Uma das vantagens ao lado da separação clara de interesses é que você pode facilmente passar parâmetros para o próximoViewModel ou executar um código de inicialização assíncrono logo após a navegação. Agora, para detalhes.

(Vou tentar simplificar todos os exemplos de código tanto quanto possível).

1. Em primeiro lugar, precisamos de um local onde possamos registrar todos os nossos objetos e, opcionalmente, definir seu tempo de vida. Para este assunto, podemos usar um contêiner IOC, você pode escolher um. Neste exemplo vou usar o Autofac (é um dos mais rápidos disponíveis). Podemos manter uma referência a ele no Apppara que esteja disponível globalmente (não é uma boa ideia, mas necessária para simplificação):

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. Precisaremos de um objeto responsável por recuperar uma Page(Visualização) para um específico ViewModele vice-versa. O segundo caso pode ser útil no caso de configuração da página raiz / principal do aplicativo. Para isso, devemos concordar com uma convenção simples de que todos os ViewModelsdevem estar no ViewModelsdiretório e Pages(Visualizações) devem estar no Viewsdiretório. Em outras palavras, ViewModelsdeve residir no [MyApp].ViewModelsnamespace e Pages(Views) no [MyApp].Viewsnamespace. Além disso, devemos concordar que WelcomeView(Página) deve ter um WelcomeViewModele etc. Aqui está um exemplo de código de um 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 o caso de configuração de uma página raiz, precisaremos ViewModelLocatordefinir o BindingContextautomaticamente:

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. Por fim, precisaremos de uma abordagem NavigationServiceque apoie 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 você pode ver, existe uma BaseViewModel- classe base abstrata para todas as ViewModelsonde você pode definir métodos como os InitializeAsyncque serão executados logo após a navegação. E aqui está um exemplo de navegação:

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 você entende, essa abordagem é mais complicada, mais difícil de depurar e pode ser confusa. No entanto, existem muitas vantagens, além disso, você não precisa implementá-lo sozinho, já que a maioria dos frameworks MVVM oferece suporte para ele fora da caixa. O exemplo de código demonstrado aqui está disponível no github .

Existem muitos artigos bons sobre ViewModel First Navigationabordagem e um eBook grátis Enterprise Application Patterns usando Xamarin.Forms que explica este e muitos outros tópicos interessantes em detalhes.

EvZ
fonte
3

Usando o método PushAsync (), você pode enviar por push e PopModalAsync () exibir páginas de e para a pilha de navegação. No meu exemplo de código abaixo, tenho uma página de navegação (página raiz) e, a partir dessa página, empurro uma página de conteúdo que é uma página de login, uma vez que concluo minha página de login, volto para a página raiz

~~~ A navegação pode ser considerada como uma pilha de objetos Página do último a entrar, primeiro a sair. Para mover de uma página para outra, um aplicativo enviará uma nova página para essa pilha. Para retornar à página anterior, o aplicativo irá retirar a página atual da pilha. Esta navegação no Xamarin.Forms é feita pela interface INavigation

Xamarin.Forms possui uma classe NavigationPage que implementa esta interface e irá gerenciar a pilha de páginas. A classe NavigationPage também adicionará uma barra de navegação ao topo da tela que exibe um título e também terá um botão Voltar apropriado da plataforma que retornará à página anterior. O código a seguir mostra como envolver um NavigationPage em torno da primeira página em um aplicativo:

Referência ao conteúdo listado acima e um link que você deve revisar para obter mais informações sobre os Formulários Xamarin, consulte a seção Navegação:

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

// Código removido para simplificar, apenas o pop é exibido

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

Navegação de uma página para outra página em Xamarin.forms usando a propriedade Navigation Abaixo do código de amostra

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 uma página para outra página com a célula de visualização Abaixo do 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));
        }

Exemplo como abaixo

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

Ligar:

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

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

public void ChangeScreen(Page page)
{
     MainPage = page;
}
alansiqueira 27
fonte
2
In App.Xaml.Cs:

MainPage = new NavigationPage( new YourPage());

Quando você deseja navegar de YourPage para a próxima página, você faz:

await Navigation.PushAsync(new YourSecondPage());

Você pode ler mais sobre a navegação de Formulários Xamarin aqui: https://docs.microsoft.com/en-us/xamarin/xamarin-forms/app-fundamentals/navigation/hierarchical

A Microsoft tem documentos muito bons sobre isso.

Existe também o conceito mais recente do Shell. Ele permite uma nova forma de estruturar seu aplicativo e simplifica a navegação em alguns casos.

Introdução: https://devblogs.microsoft.com/xamarin/shell-xamarin-forms-4-0-getting-started/

Vídeo sobre os fundamentos do 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
fonte
0

Página XAML adicionar isto

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

</ContentPage.ToolbarItems>   

na página CS

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

Depois de PushAsyncusar PopAsync(com this) para remover a página atual.

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

No Xamarin temos uma página chamada NavigationPage. Ele contém pilha de ContentPages. NavigationPage tem métodos como PushAsync () e PopAsync (). PushAsync adiciona uma página no topo da pilha, nesse momento essa página se tornará a página ativa no momento. O método PopAsync () remove a página do topo da pilha.

Em App.Xaml.Cs podemos definir como.

MainPage = nova NavigationPage (new YourPage ());

esperar Navigation.PushAsync (new newPage ()); este método adicionará newPage ao topo da pilha. Neste momento, o nePage será a página ativa no momento.

PRASAD CP
fonte