Tutorial Windows Phone #7 – Navegação com Estados

Padrão

Olá pessoal leitores!!!

No post de hoje, vamos avançar um pouco mais no desenvolvimento dos aplicativos para a plataforma Windows Phone. Nos tutoriais anteriores, vimos como transferir parâmetros entre páginas, realizar transições e utilizar configurações gerais compartilhadas entre todas as páginas. Hoje, vamos ver como ocorre a parametrização de objetos.

Você poderia sugerir que, assim como vimos no tutorial 5, poderíamos transferir objetos campo a campo. Sim, é possível, mas seria bem mais trabalhoso. Além disso, a informação que trafega entre as páginas utilizando o método exibido no tutorial 5 limita o tamanho dos dados a 64kb. É bastante coisa, mas pode não ser suficiente em algumas situações.

No aplicativo de hoje, vamos ter uma tela inicial com um link para uma segunda página. Nesta segunda página, haverá um pequeno formulário, onde serão inseridas alguns dados, como nome e cidade. Ao salvar estes dados, retornando a tela inicial, será exibido o nome e a cidade inseridos na outra página.

Para começarmos, crie um novo projeto e adicione uma segunda página, chamada Pagina2.xaml, assim como fizemos nos demais tutoriais. Fique à vontade em alterar o título das páginas para melhorar a navegação e ficar mais claro onde estamos no aplicativo.

Bom, neste tutorial, precisaremos de utilizar uma classe criada por nós, para representar as informações que inserirmos no formulário. Vamos, então, criar uma classe chamada Pessoa. Para isso, clique com o botão direito sobre o projeto, selecione a opção Add -> New Item…. Na janela, procure por Class e nomeie para Pessoa.cs.

Vamos agora inserir três atributos e o construtor da nossa classe. Os atributos serão NomeSobrenome Telefone, todos do tipo string. O construtor será vazio, sem qualquer argumento. Todos os atributos terão seus métodos de acesso e atribuição públicos. A classe completa fica assim:

namespace NavegacaoComEstados
{
    public class Pessoa
    {
        public string Nome { get; set; }
        public string Sobrenome { get; set; }
        public string Cidade { get; set; }

        public Pessoa() { }
    }
}

Agora, vamos até a nossa página principal, MainPage.xaml, para adicionarmos os seguintes itens ao nosso ContentPanel.

<!--ContentPanel - place additional content here-->
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
    <TextBlock x:Name="tbMsg" />
    <HyperlinkButton x:Name="hlNavegacao" Content="Ir para a Página 2"
                     Click="hlNavegacao_Click" />
</Grid>

Adicionamos um TextBlock que exibirá as informações inseridas no formulário e um HyperlinkButton, que direcionará para a próxima página. Agora, no MainPage.xaml.cs, vamos implementar o método PhoneApplicationPage_Loaded() que será chamado ao carregar-se a página principal e será encarregado de verificar se há informações para serem exibidas no TextBlock.

private void PhoneApplicationPage_Loaded(object sender, RoutedEventArgs e)
{
    if (PhoneApplicationService.Current.State.ContainsKey("Pessoa"))
    {
        Pessoa p = (Pessoa)PhoneApplicationService.Current.State["Pessoa"];
        tbMsg.Text = string.Format("Olá {0} {1} de {2}", p.Nome, p.Sobrenome, p.Cidade);
    }
}

Para esse método, é necessário adicionar a cláusula using Microsoft.Phone.Shell. Nele, verificamos a existência da chave Pessoa no State da aplicação (que funciona mais ou menos como a sessão dos aplicativos web). Caso positivo, obtemos o objeto, fazemos o cast  para o objeto p e atribuímos no tbMsg. Para fecharmos a página principal, vamos também implementar a navegação para a página seguinte. Nesse ponto, não devem haver novidades para vocês.

private void hlNavegacao_Click(object sender, RoutedEventArgs e)
{
    this.NavigationService.Navigate(new Uri("/Pagina2.xaml", UriKind.Relative));
}

Pronto. Lembre-se também de adicionar a propriedade Loaded no atributo phone:PhoneApplicationPage do MainPage.xaml, para que o método que implementamos seja, de fato, chamado ao completar-se o carregamento da página.

<phone:PhoneApplicationPage
    x:Class="NavegacaoComEstados.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
    xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d" d:DesignWidth="480" d:DesignHeight="768"
    FontFamily="{StaticResource PhoneFontFamilyNormal}"
    FontSize="{StaticResource PhoneFontSizeNormal}"
    Foreground="{StaticResource PhoneForegroundBrush}"
    SupportedOrientations="Portrait" Orientation="Portrait"
    shell:SystemTray.IsVisible="True" Loaded="PhoneApplicationPage_Loaded">

Vamos agora para a Pagina2.xaml. Criaremos um formulário simples, com os três campos (Nome, Sobrenome e Telefone) e um botão para confirmar. Adicione o seguinte conteúdo ao ContentPanel do arquivo.

<!--ContentPanel - place additional content here-->
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
    <StackPanel>
        <TextBox x:Name="txtNome" Text="Nome" GotFocus="txt_GotFocus" />
        <TextBox x:Name="txtSobrenome" Text="Sobrenome" GotFocus="txt_GotFocus" />
        <TextBox x:Name="txtCidade" Text="Cidade" GotFocus="txt_GotFocus" />
        <Button x:Name="btnSalvar" Content="Salvar" Click="btnSalvar_Click" />
    </StackPanel>
</Grid>

Todos os TextBoxes implementam o método GotFocus. Assim, vamos fazer com que o texto deles seja selecionado ao receberem o foco. Implemente o método txt_GotFocus() no Pagina2.xaml.cs:

private void txt_GotFocus(object sender, RoutedEventArgs e)
{
    TextBox txt = sender as TextBox;
    txt.SelectAll();
}

Quando tocarmos sobre o botão Salvar, devermos salvar os dados que estiverem no formulário ou então atualizar o que já está salvo. Para esse propósito, vamos implementar o método SalvarOuAtualizar().

private void SalvarOuAtualizar()
{
    Pessoa p = null;

    if (PhoneApplicationService.Current.State.ContainsKey("Pessoa"))
    {
        p = (Pessoa)PhoneApplicationService.Current.State["Pessoa"];
        p.Nome = txtNome.Text;
        p.Sobrenome = txtSobrenome.Text;
        p.Cidade = txtCidade.Text;
    }
    else
    {
        p = new Pessoa();
        p.Nome = txtNome.Text;
        p.Sobrenome = txtSobrenome.Text;
        p.Cidade = txtCidade.Text;
    }

    PhoneApplicationService.Current.State["Pessoa"] = p;
}

Neste método, verificamos se já existe uma chave chamada Pessoa do State da aplicação. Caso positivo, obtemos o objeto através da chave e atualizamos seus atributos. Caso contrário, criamos um novo objeto com os dados recém lidos do formulário. Ao final do método, colocamos o objeto no State sob a chave Pessoa.

Agora, vamos implementar um artifício para evitar a perda de dados, caso o usuário pressione a tecla Home. Nesse caso, o aplicativo não foi encerrado, mas está apenas em estado de espera, rodando em segundo plano. Ao restaurar a execução, ele deve voltar com os dados. Para isso, vamos implementar os métodos OnNavigatedFrom() para quando o usuário “minimiza” o aplicativo, e o método OnNavigatedTo() para quando o usuário retorna ao aplicativo. Para simplificar o processo de salvamento, vamos criar também o método SalvarDadosDaPagina().

protected override void OnNavigatedFrom(System.Windows.Navigation.NavigationEventArgs e)
{
    if (e.NavigationMode != System.Windows.Navigation.NavigationMode.Back)
    {
        SalvarDadosDaPagina();
    }
    else
    {
        this.State["Pessoa"] = null;
    }

    base.OnNavigatedFrom(e);
}

private void SalvarDadosDaPagina()
{
    Pessoa p = new Pessoa();
    p.Nome = txtNome.Text;
    p.Sobrenome = txtSobrenome.Text;
    p.Cidade = txtCidade.Text;
    this.State["Pessoa"] = p;
}

protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
{
    if (this.State.ContainsKey("Pessoa"))
    {
        Pessoa p = (Pessoa)this.State["Pessoa"];
        txtNome.Text = p.Nome;
        txtSobrenome.Text = p.Sobrenome;
        txtCidade.Text = p.Cidade;
    }
    else if (PhoneApplicationService.Current.State.ContainsKey("Pessoa"))
    {
        Pessoa p = (Pessoa)PhoneApplicationService.Current.State["Pessoa"];
        txtNome.Text = p.Nome;
        txtSobrenome.Text = p.Sobrenome;
        txtCidade.Text = p.Cidade;
    }

    base.OnNavigatedTo(e);
}

Basicamente, na situação de sairmos da aplicação, caso não tenha sido realizado com o botão voltar (que no caso, voltaria para a tela inicial), salvamos os dados atuais em um objetos e colocamos no State. Quando voltamos, verificamos se existe algum objeto armazenado no State geral ou no State da aplicação. Caso exista, o colocamos de volta no formulário. 🙂

Por último, vamos implementar o método btnSalvar_Click() que é chamado ao tocarmos o botão Salvar. Como já temos o método SalvarOuAtualizar() implementado, basta chamá-lo e retornar para a página principal.

private void btnSalvar_Click(object sender, RoutedEventArgs e)
{
    SalvarOuAtualizar();
    this.NavigationService.GoBack();
}

Ufa! É isso. Vamos executar para ver o que acontece 😀

Brinque e teste o aplicativo para compreender bem o que foi implementado. Ah, e pra baixar o projeto, é só clicar aqui.