A Universal Windows Platform (UWP) é uma plataforma unificada para o desenvolvimento de aplicações que rodem em dispositivos que executam o Windows 10, como tablets, smartphones, computadores e o Xbox. Claro que por terem diferentes capacidades e componentes, em alguns casos é necessário desenvolver um código específico para cada dispositivo, mas a API de desenvolvimento do UWP facilita muito esse trabalho, evitando ter que reescrever a aplicação para cada plataforma.
Quando desenvolvemos aplicações para o Windows 8 escolhemos em qual plataforma a mesma seria executada (desktop ou no Windows Phone). Na versão atual escolhemos para quais famílias de dispositivos a aplicação será executada, como computadores (notebook e desktop), dispositivos móveis (smartphones e tablets), Xbox, entre outras. Todas elas possuem a API da UWP e um subconjunto de APIs específicas para cada tipo de dispositivo.
Caso a aplicação não utilize nenhuma API específica, ela pode ser utilizada em qualquer dispositivo que execute o Windows 10, como é o caso da que desenvolveremos nesse artigo.
A aplicação terá três interfaces: a primeira para listar a atividade, uma para o cadastro de pessoas e uma para a listagem das pessoas cadastradas. Todo o código será desenvolvido com o Visual Studio Community 2015, que é livre para fins de estudos. Caso queira desenvolver aplicações comerciais com essa ferramenta, na seção Links encontram-se as informações sobre licenças do Visual Studio.
Desenvolvimento da Aplicação
Em uma aplicação UWP utilizamos as linguagens C# e XAML (Extensible Application Markup Language) para o desenvolvimento das interfaces de usuários. Esta última é uma linguagem de marcação simples que contém formulários, listas e tabelas, ideais para a criação de UI. Todo XAML tem uma classe C# associada chamada de Code behind, que controla os eventos que acontecem na visão. A Listagem 1 mostra o código básico de um XAML.
<Page
x:Class="App5.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:App5"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
</Grid>
</Page>
Para iniciar o desenvolvimento criamos um projeto do tipo Universal Windows no Visual Studio, como mostra a Figura 1.
Com isso já é criado o primeiro arquivo XAML, que terá as interfaces com o usuário. A página inicial MainPage terá apenas três botões de navegação para as interfaces de cadastro, listagem e busca. Em seguida, devemos criar uma classe simples chamada Pessoa, que tem apenas os atributos Nome e Telefone e seus métodos get e set, como mostra o código da Listagem 2.
namespace App5
{
public class Pessoa
{
public string Nome { set; get; }
public string Telefone { set; get; }
}
}
Para organizar a disposição dos elementos dentro da tela utilizaremos o código da Listagem 3, que exibe o código do XAML da tela principal. Este utiliza o componente <StackPanel>. Com isso eles ficam organizados em um painel como uma pilha, um abaixo do outro. Dentro do painel existem dois botões declarados com o componente . A configuração para cada um deve ser:
- O atributo Content define o texto que será exibido dentro do botão;
- O atributo Margin define o espaçamento entre os botões;
- O evento Click define o método que será chamado na classe code-behind quando um usuário clicar no botão.
<Page
x:Class="App5.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:App5"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<StackPanel Name="Panel" VerticalAlignment="Center" HorizontalAlignment="Center">
<TextBlock Name="Titulo" Text="Lista de Telefone" FontSize="30"
HorizontalAlignment="Center" />
<Button Name="btCadastrar" Click="cadastrar_action" Margin="20" Content="Cadastrar"
Width="100" HorizontalAlignment="Center"/>
<Button Name="btListar" Click="listar_action" Margin="20" Content="Listar" Width="100"
HorizontalAlignment="Center"/>
</StackPanel>
</Grid>
</Page>
Outra característica interessante do Visual Studio é o desenvolvimento dos XAML sem a necessidade de programação, apenas utilizando drag and drop de componentes e editando algumas características visuais na própria visualização da tela. Ele permite também a alteração de diversas propriedades dos componentes na aba Properties. A Figura 2 mostra uma imagem da visualização da interface.
Para cada botão que faz a navegação da página principal para as outras páginas da aplicação temos a codificação da Listagem 4, que mostra o código da classe code-behind do XAML MainPage, com um método para cada botão.
O trecho de código this.Frame.Navigate(typeof(Cadastro)) define a navegação da página atual para a página cadastro (que ainda vamos criar) e os outros métodos fazem a mesma coisa para as outras telas da aplicação.
namespace App5
{
/// <summary>
/// An empty page that can be used on its own or navigated to within a Frame.
/// </summary>
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
}
// faz a navegação para tela Cadastrar
public void cadastrar_action(object sender, RoutedEventArgs e)
{
this.Frame.Navigate(typeof(Cadastrar));
}
// faz a navegação para tela listar
public void listar_action(object sender, RoutedEventArgs e)
{
this.Frame.Navigate(typeof(Listar));
}
}
}
A Listagem 5 mostra o código da tela de cadastro que tem dois campos de texto declarados com a tag : um campo para o cadastro do nome da pessoa e o outro para o cadastro do telefone. Além disso existe um Label declarado com a tag com o título da página “Cadastro de Contato”. Temos também um botão de onde a ação de cadastrar a pessoa será executada.
<Page
x:Class="App5.Cadastrar"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:App5"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<StackPanel Name="Panel" VerticalAlignment="Center" HorizontalAlignment="Center">
<TextBlock Name="Titulo" Text="Cadastro de Pessoa" HorizontalAlignment="Center" />
<TextBox PlaceholderText="Nome" Name="Nome" Width="138" Margin="20" ></TextBox>
<TextBox PlaceholderText="Telefone" Name="Telefone" Width="138" Margin="20" >
</TextBox>
<Button Name="btCadastrar" Margin="20" Content="Cadastrar" HorizontalAlignment="Center"
Click="Button_Click" Width="100"/>
</StackPanel>
</Grid>
</Page>
Nessa tela também é utilizado um que é colocado no centro da tela com os atributos VerticalAlignment e HorizontalAlignment. No componente são utilizados os seguintes atributos:
- PlaceholderText, que exibe um texto inicial para o componente que é apagado assim que o usuário clica nele;
- Width, que define o tamanho do campo na tela;
- Margin, para definir o espaçamento entre os campos do formulário;
- E o atributo Name para definir o nome do componente, já que esses campos serão chamados na classe code-behind dessa tela.
A Listagem 6 mostra esse code-behind da tela de cadastro. Nele existe o método Button1_click, que será executado a partir da ação de click do botão da tela de cadastro. No código deste é feita a adição dos dados da pessoa em uma lista de pessoas que está declarada na própria classe. Como esta lista é estática, ela pode ser acessada de qualquer parte da aplicação.
Para essa aplicação não vamos utilizar um banco de dados, mas fique à vontade para adaptar a mesma com o SQLite, por exemplo.
Depois do cadastro da pessoa é feita a navegação de volta para a tela inicial da aplicação.
namespace App5
{
/// <summary>
/// An empty page that can be used on its own or navigated to within a Frame.
/// </summary>
public sealed partial class Cadastrar : Page
{
public Cadastrar()
{
this.InitializeComponent();
}
//ação do botão cadastrar. Cadastra a pessoa e volta para a tela inicial
private void Button_Click(object sender, RoutedEventArgs e)
{
Pessoa pessoa = new Pessoa();
pessoa.Nome = Nome.Text;
pessoa.Telefone = Telefone.Text;
App.pessoas.Add(pessoa);
this.Frame.Navigate(typeof(MainPage));
}
}
}
A outra tela da aplicação lista todas as pessoas cadastradas usando o Data Binding, que é um conceito presente na maioria das plataformas hoje em dia. Este permite a vinculação de elementos de código com elementos da visão do usuário, diminuindo a necessidade de código escrito.
A Listagem 7 mostra o código fonte do XAML da tela de listagem de pessoas, que usa o padrão ViewModel. Este inclui uma classe que é responsável por recuperar os dados e enviá-los para a página através da tag . Além disso, é necessário adicionar o atributo xmlns:vm="using:App5" na tag , onde o valor using:App5 é o namespace da classe ViewModel.
Depois é declarado o componente ListView, que exibe uma lista de Items. O Data Binding é feito no atributo ItemsSource, vinculado a um objeto chamado Items, que será declarado na classe do ViewModel desta tela.
Existe também uma vinculação no atributo Header, onde será passado o título da listagem, assim essa tela pode ser genérica e usada em qualquer listagem de pessoas. Os componentes são vinculados aos atributos Nome e Telefone declarados na classe Pessoa.
<Page
x:Class="App5.Listar"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:App5"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:vm="using:App5"
mc:Ignorable="d">
<Page.DataContext>
<vm:PessoaViewModel />
</Page.DataContext>
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<ListView Name="lvPessoas" ItemsSource="{Binding Items}" Header="{Binding ListName}"
Grid.Column="0">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Name="tbNome" Text="{Binding Nome}" FontSize="20"/>
<TextBlock Name="tbTelefone" Text="{Binding Telefone}" FontSize="16" />
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<StackPanel Name="Panel" VerticalAlignment="Center" HorizontalAlignment="Right"
Grid.Column="1">
<Button Name="btVoltar" Margin="20" Content="Voltar" Width="100" Click="Button_Click"/>
</StackPanel>
</Grid>
</Page>
Para recuperar os dados e exibi-los na tela de listagem é necessário criar a classe ViewModel, presente no código da Listagem 8.
No próprio construtor da classe é recuperado os dados da lista de pessoas que são adicionados na lista Items. Esta é vinculada ao Data Binding pelo ListView da tela mostrada anteriormente.
A classe ObservableCollection funciona como um List, mas é utilizada quando é feito DataBinding. Como vantagem, caso ela seja modificada, a classe notifica automaticamente a View, que é atualizada com as modificações. Para utilizá-la é necessário declarar o namespace System.Collections.ObjectModel.
Além disso, existe a string ListName, que mostra o título a ser exibido no ListView da tela de listagem das pessoas.
using System.Collections.ObjectModel;
namespace App5
{
class PessoaViewModel
{
//Coloca as pessoas na lista vinculada a visão
public PessoaViewModel()
{
ListName = "Lista de Pessoas";
for (int i = 0; i < App.pessoas.Count; i++)
{
Pessoa pessoa = App.pessoas.ElementAt(i);
this.Items.Add(pessoa);
}
}
//lista de pessoas que serão exibidas na tela
public ObservableCollection<Pessoa> Items
{
get; set;
} = new ObservableCollection<Pessoa>();
public string ListName { get; set; }
}
}
A classe code-behind da tela de listagem, presente na Listagem 9, tem apenas a ação que é disparada quando o usuário aperta o botão Voltar. Esse método volta para a tela MainPage.
namespace App5
{
/// <summary>
/// An empty page that can be used on its own or navigated to within a Frame.
/// </summary>
public sealed partial class Listar : Page
{
public Listar()
{
this.InitializeComponent();
}
//faz a navegação de volta para a tela principal
private void Button_Click(object sender, RoutedEventArgs e)
{
this.Frame.Navigate(typeof(MainPage));
}
}
}
A lista de pessoas que são salvas na tela Cadastrar e que são exibidas na tela de listagem foi declarada na classe App que todo projeto da UWP tem. Essa classe é utilizada para fazer algumas configurações iniciais da aplicação e também para controlar o ciclo de vida da mesma, por meio de alguns métodos:
- OnLaunched, para definir o comportamento da aplicação assim que ela é iniciada;
- OnNavigationFailed, que define o comportamento quando a navegação falha;
- OnSuspending, que define o que fazer quando a execução da aplicação é suspensa.
A Listagem 10 mostra o código que deve ser adicionado nessa classe. Este pode ser adicionado em qualquer posição que esteja fora de um método dentro da classe, mas recomendamos que seja logo no início da classe.
sealed partial class App : Application
{
public static List<Pessoa> pessoas = new List<Pessoa>();
//outro código aqui gerado automaticamente pelo Visual Studio
}
A Figura 3 mostra a aplicação sendo executada como uma aplicação normal do Windows: a direita está a tela principal com os botões para a navegação entre as telas; no centro a tela para cadastro de pessoas com os componentes de formulário e o botão cadastrar; e por último, a esquerda, a tela de listagem de pessoas cadastradas.
Essa aplicação pode ser executada também no emulador do Windows Phone. A Figura 4 mostra as opções para a execução da aplicação no Visual Studio. Repare que é possível rodar a app na máquina local, em uma máquina remota, em um simulador e nos emuladores do Windows Phone.
Caso tenha um dispositivo Windows Phone disponível, é possível executar a aplicação diretamente no mesmo.
Com isso vimos que a API UWP é uma ferramenta muito poderosa para o desenvolvimento de aplicações voltadas a família de dispositivos Microsoft. Além de ser muito fácil desenvolver uma aplicação nesta plataforma, também é possível executar a mesma versão da aplicação, com nenhuma ou muito pouca alteração no código, em vários dispositivos diferentes, tudo isso unido a IDE Visual Studio.