Muitos aplicativos Windows Phone são compostos por várias páginas e a navegação entre elas é relativamente simples, exigindo pouco esforço de programação. Mas no decorrer do desenvolvimento do projeto nos deparamos com problemas e comportamentos indesejáveis e, depois de algum tempo de investigação, acabamos descobrindo que esses problemas estão relacionados justamente com a forma como implementamos a navegação entre páginas.
Portanto, apesar de ser simples, a navegação entre páginas pode trazer um pouco de dor de cabeça para o desenvolvedor, principalmente quando este esquece de testar alguns cenários comuns como aquele em que seu aplicativo é suspenso por causa de uma ligação recebida pelo usuário.
A seguir iremos contextualizar os aspectos relacionados com a suspensão do aplicativo e navegação entre páginas. Depois iremos abordar como realizar a passagem de parâmetros entre páginas. E por fim citaremos problemas normalmente encontrados pelo desenvolvedor:
- Erro durante a navegação entre páginas.
- Erro quando o aplicativo retorna de um estado de suspensão ou de término.
Gerenciando a suspensão do aplicativo e a navegação entre páginas
Para facilitar o trabalho do desenvolvedor, o Visual Studio 2013 (para desenvolvimento de aplicações Windows e Windows Phone 8.1) oferece, em alguns dos seus templates, classes que facilitam o controle de navegação entre páginas e também o gerenciamento de informações quando seu aplicativo é suspenso.
Essas classes são automaticamente incluídas no seu projeto quando você o cria usando os seguintes templates: Hub App (Universal Apps), Hub App (Windows Phone), Pivot App (Windows Phone).
Essas classes são:
Common.NavigationHelper.cs
: Possui recursos para navegação entre páginas, já tratando o salvamento e carga do estado da página.Common.SuspensionManager.cs
: Fornece código para salvar informações do aplicativo quando o mesmo é suspenso e depois retomado.
Além disso, para esses templates são incluídas alterações pontuais no arquivo App.xaml.cs
para preparar seu aplicativo para usar tanto o NavigationHelper
como o SuspensionManager
:
- No método
OnLaunched
é incluído o modificadorasync
e também o seguinte código:protected async override void OnLaunched(LaunchActivatedEventArgs e) { #if DEBUG if (System.Diagnostics.Debugger.IsAttached) { this.DebugSettings.EnableFrameRateCounter = true; } #endif Frame rootFrame = Window.Current.Content as Frame; // Do not repeat app initialization when the Window already has content, // just ensure that the window is active if (rootFrame == null) { // Create a Frame to act as the navigation context and navigate to the first page rootFrame = new Frame(); // Added: Associate the frame with a SuspensionManager key SuspensionManager.RegisterFrame(rootFrame, "AppFrame"); // TODO: change this value to a cache size that is appropriate for your application rootFrame.CacheSize = 1; // ...
Nota: Nós devemos registrar o
Frame
da aplicação noSuspensionManager
. A partir deste momento, sempre que a aplicação for suspensa, oSuspensionManager
fornece um meio simplificado do desenvolvedor prover código para salvar o estado da aplicação. Dessa forma, quando a aplicação retornar, o desenvolvedor pode recuperar o estado salvo. - No método
OnSuspending
é incluído o modificadorasync
e também o seguinte código:private async void OnSuspending(object sender, SuspendingEventArgs e) { var deferral = e.SuspendingOperation.GetDeferral(); // Added await SuspensionManager.SaveAsync(); deferral.Complete(); }
Adicionando recursos de suspensão e navegação em um código já existente
É indicado a criação de um projeto a partir de um template pois este já oferece os recursos básicos que facilitam o desenvolvimento. Mas caso você tenha criado sua aplicação não usando os templates citados acima e queira incluir o SuspensionManager
e NavigationHelper
no código, você poderá fazê-lo incluindo uma página usando o template Basic Page.
Nesse caso o próprio Visual Studio inclui as classes no seu projeto. Mas lembre-se que você precisa, neste caso, incluir manualmente as alterações na classe App.xaml.cs
.
Passando parâmetros entre páginas
Para navegar entre as páginas, devemos usar o método Navigate(Type pageType)
. E para incluir parâmetros, usamos o método Navigate(Type pageType, object param)
.
Por exemplo, considere uma página chamada BasicPage1
. Desejamos a partir dela navegar para outra página passando um parâmetro. Para tanto:
- Na classe
BasicPage1
:private void HyperlinkButton_Click(object sender, RoutedEventArgs e) { this.Frame.Navigate(typeof(BasicPage2), tb1.Text); }
Notas:
- A
BasicPage2
é a classe da página para a qual desejamos navegar.- O
tb1
é um TextBox incluído na página de origem para que o usuário forneça um texto que será enviado como parâmetro. Segue o trecho do código XAML da página de origem que inclui esse campo e o hyperlink:<TextBox HorizontalAlignment="Stretch" Name="tb1" Margin="0"/> <HyperlinkButton Content="Click to go to page 2" Click="HyperlinkButton_Click"/>
- Na classe
BasicPage2
:private void NavigationHelper_LoadState(object sender, LoadStateEventArgs e) { string message = e.NavigationParameter as string; if (!string.IsNullOrWhiteSpace(name)) { tb1.Text = "Hello, " + name; } }
Notas:
- Implementamos um método para manipular o evento
LoadState
que é disponibilizado pela classeNavigationHelper
. Nesse método obtemos o parâmetro e fazemos o cast para o tipo correspondente. No exemplo esse tipo éstring
.- O
tb1
é um TextBlock que foi incluído na página BasicPage2.xaml para visualizar o valor do parâmetro.
Problemas Comuns de Desenvolvimento
Os problemas normalmente encontrados na passagem de parâmetros entre páginas são:
-
Erro “GetNavigationState doesn’t support serialization of a parameter type which was passed to Frame.Navigate“
Isso está relacionado com o tipo de parâmetro que tentamos passar entre páginas. Um problema muito comum é não usar um tipo que seja serializável (isto é, convertido em texto e depois recuperado do texto).
Para evitar este problema, lembre-se sempre de passar como parâmetros objetos de tipos não complexos. Os tipos básicos como
string
eint
já são serializáveis. E se criamos uma classe nossa e desejamos passar como parâmetro na navegação entre páginas, devemos garantir que as informações nela contidas sejam serializáveis. Um exemplo de tipo que deve ser evitado é oObservableCollection
. Mais detalhes sobre esse problema podem ser obtidos na página de descrição do métodoFrame.Navigate
.Uma forma que as vezes encontramos como sugestão para passar parâmetros de diferentes tipos entre página é serializar o objeto usando a biblioteca Json.Net. Dessa forma, passamos como parâmetro a
string
que representa o objeto e depois, na outra página, obtermos esse mesmostring
e deserializamos usando a própria biblioteca Json.Net. - Erro ao resumir a aplicação, sendo a mesma encerrada sem nenhum erro aparente.
Isso está relacionado mais com o tipo do parâmetro fornecido nas navegações entre páginas. Quando navegamos fornecendo um parâmetro, devemos garantir que o tipo é conhecido pela classe
SuspensionManager
. Para isso, devemos disponibilizar o seguinte código no construtor da nossa aplicação, noApp.xaml.cs
:public App() { this.InitializeComponent(); this.Suspending += this.OnSuspending; SuspensionManager.KnownTypes.Add(typeof(MyCustomType)); }
Lembre-se que esse erro só será verificado quando a aplicação for resumida (após ter sido suspensa ou terminada). E é aí que reside o problema. Esse teste muitas vezes passa desapercebido pelo desenvolvedor. Além disso, não importa a quantidade de páginas que temos na nossa aplicação, se pelo menos 1 página passar como parâmetro um tipo desconhecido, o erro ocorrerá quando a aplicação tentar voltar depois de ter sido suspensa.
Para testar esse caso, podemos usar o próprio recurso do Visual Studio de ciclo de vida da aplicação enquanto fazemos depuração de nosso código:
Segue procedimento para testar contra este erro:
- Iniciar a depuração da sua aplicação (no emulador ou no dispositivo).
- Para cada página que você navegar, selecione o evento de ciclo de vida “Suspend”. Seu aplicativo será suspenso no dispositivo ou emulador.
- Após suspender a aplicação, tentar retornar para ela através da opção “Resume” ou ainda acessando a aplicação a partir da lista de aplicativos instalados no dispositivo ou emulador. Como resultado esperado, o seu aplicativo deve retornar sem problemas.
Portanto, antes de publicar seu aplicativo, tenha certeza que seu aplicativo não possui nenhum desses problemas citados acima.
Até mais! Continue nos acompanhando pelo blog talkitbr.
Olá fiz o exemplo que vc passou usando o frame.navigate para enviar deu tudo certo, mas ao receber os parâmetros na tela que estou direcionando quando tento enviar um segundo objeto da erro, minha pergunta é como tratar esse recebimento de parâmetros ?
CurtirCurtir
Olá Rodrigo, obrigado pelo contato!
Quando se repassa parâmetros para outra página, o que você recebe do outro ládo é um
object
. Você precisa fazer o cast do objeto para o tipo correto no outro lado. Por exemplo, usando o template padrão do HubApp, na página HubPage posso especificar o seguinte:Já na página destino, que é o GroupPage, especifico o seguinte:
Att,
João
CurtirCurtir
Olá João Boa Noite e obg pelo retorno.
Essa parte que vc explicou eu acho que já estou fazendo. No total de telas são 3 a de pedido(principal), a de cliente e a de produtos. Vou colocar o exemplo.
pagina Cliente ao ser clicado:
pagina pedido (recebe o objeto)
dentro dessa tela pedido eu vou pra outra tela buscar os produtos:
evento gerando na tela produtos que envia outro objeto para a tela de pedidos.
retorno para tela pedido paea receber o objeto produto
se eu fizer o mesmo procedimento de recebimento de objeto que fiz com o cliente na tela de pedido da um erro pq já estou recebendo o objeto cliente primeiro, minha duvida era como tratar esse segundo objeto na tela de pedidos.
Não sei se consegui passar minha duvida corretamente mais é isso. Mais uma vez obrigado pela atenção e desculpe pelo abuso.
CurtirCurtir
Olá Rodrigo,
Entendi o problema. Considerando sua abordagem, é possível contornar seu problema da seguinte forma:
1. Crie uma classe Pedido (ou algum outro nome similar) que contém as propriedades Cliente e Produto.
2. Na página Cliente você cria esse objeto, define a propriedade cliente e então repassa o objeto pedido como parâmetro para a tela pedido.
3. Na tela Pedido, extraia o objeto pedido do parâmetro de navegação e atribua a uma variável local ou num ViewModel.
4. Quando você for navegar para a tela Produto, repasse o mesmo objeto pedido (que já contem o cliente) como parâmetro de navegação.
5. Na tela Produto, extraia o objeto pedido do parâmetro de navegação e, quando o usuário selecionar o produto, defina a propriedade produto no objeto pedido.
4. Quando você for navegar de volta para a tela Pedido, repasse o mesmo objeto pedido (que agora tem cliente e produto) como parâmetro de navegação.
A única questão que me preocupa na sua solução é como fica a pilha de navegação no final de todo o processo. Quando o usuário navega novamente para a página Pedido com o produto selecionado, se ele clicar em Voltar, me parece que a aplicação retornará para a tela Produto, depois Pedido e depois Cliente. Se for esse o caso, sugiro a seguinte abordagem:
Salvar o Pedido (cliente e produto) no
SuspensionManager.SessionState
. Nesse caso não precisa manter a informação no parâmetro de navegação. E então, na tela Produto, usar oFrame.GoBack();
para sair da página;CurtirCurtir
Olá João segui o que vc falou e deu certo , mais pesquisando achei um outro jeito que tbm resolveu meu problema. Na minha pagina principal(pedido) eu faço o seguinte tratamento no onNavigateTo.
if (e.Parameter.GetType() == typeof(Cliente)) {
//Faz o que deve ser feito para o cliente
else if (e.Parameter.GetType() == typeof(Produto)) {
//Faz o que deve ser feito com o produto
}
else {
//Recebeu um tipo não esperado
}
Obg pela orientação.
OBS:
A plataforma de desenvolvimento para Windowsphone parece ser bem promissora mais ainda é bem complicado arranjar informações quando se tem duvidas, se não for com pessoas como vc fica muito difícil. Acho que a microsoft deveria disponibilizar um material (tutoriais, vídeo aulas no youtube, etc) para motivar o desenvolvimento eles até tem um canal mais além de ser em inglês é pouco pratico e muito teórico.
CurtirCurtir