Criar um aplicativo .NET MAUI
Esta série de tutoriais foi projetada para demonstrar como criar um aplicativo de interface do usuário do aplicativo .NET multiplataforma (.NET MAUI) que usa apenas código multiplataforma. Ou seja, o código que você escreve não será específico para Windows, Android, iOS ou macOS. O aplicativo que você criará será um aplicativo de observação, no qual o usuário pode criar, salvar e carregar várias anotações.
Neste tutorial, você aprenderá a:
- Crie um aplicativo .NET MAUI Shell.
- Execute seu aplicativo na plataforma escolhida.
- Defina a interface do usuário com xAML (linguagem de marcação de aplicativo eXtensible) e interaja com elementos XAML por meio do código.
- Crie exibições e associe-as aos dados.
- Use a navegação para mover de e para páginas.
Você usará o Visual Studio 2022 para criar um aplicativo com o qual você pode inserir uma nota e salvá-la no armazenamento do dispositivo. O aplicativo final é mostrado abaixo:
Criar um projeto
Antes de começar este tutorial, você deve seguir Criar seu primeiro artigo de aplicativo. Ao criar o projeto, use as seguintes configurações:
Nome do Projeto
Isso deve ser definido como
Notes
. Se o projeto tiver o nome de algo diferente, o código que você copiar e colar deste tutorial poderá resultar em erros de build.Coloque a solução e o projeto no mesmo diretório
Desmarque essa configuração.
Escolha a estrutura do .NET mais recente ao criar seu projeto.
Selecionar o dispositivo de destino
Os aplicativos .NET MAUI são projetados para serem executados em vários sistemas operacionais e dispositivos. Você precisará selecionar qual destino deseja testar e depurar seu aplicativo.
Defina o Destino de Depuração na barra de ferramentas do Visual Studio para o dispositivo com o qual você deseja depurar e testar. As etapas a seguir demonstram como definir o Destino de Depuração para Android:
- Selecione o botão suspenso Destino de Depuração.
- Selecione o item Android Emulators.
- Selecione o dispositivo emulador.
Personalizar o shell do aplicativo
Quando o Visual Studio cria um projeto MAUI do .NET, quatro arquivos de código importantes são gerados. Elas podem ser vistas no painel Gerenciador de Soluções do Visual Studio:
Esses arquivos ajudam a configurar e executar o aplicativo MAUI do .NET. Cada arquivo serve a uma finalidade diferente, descrita abaixo:
MauiProgram.cs
Este é um arquivo de código que inicializa seu aplicativo. O código nesse arquivo serve como o ponto de entrada entre plataformas do aplicativo, que configura e inicia o aplicativo. O código de inicialização do modelo aponta para a classe
App
definida pelo arquivo App.xaml.App.xaml e App.xaml.cs
Apenas para manter as coisas simples, ambos os arquivos são conhecidos como um único arquivo. Geralmente, há dois arquivos com qualquer arquivo XAML, o arquivo .xaml em si e um arquivo de código correspondente que é um item filho dele no Gerenciador de Soluções. O arquivo .xaml contém marcação XAML e o arquivo de código contém o código criado pelo usuário para interagir com a marcação XAML.
O arquivo App.xaml contém recursos XAML em todo o aplicativo, como cores, estilos ou modelos. O arquivo App.xaml.cs geralmente contém código que cria uma instância do aplicativo Shell. Neste projeto, ele aponta para a classe
AppShell
.AppShell.xaml e AppShell.xaml.cs
Esse arquivo define a classe
AppShell
, que é usada para definir a hierarquia visual do aplicativo.MainPage.xaml e MainPage.xaml.cs
Esta é a página de inicialização exibida pelo aplicativo. O arquivo MainPage.xaml define a interface do usuário (interface do usuário) da página. MainPage.xaml.cs contém o code-behind para o XAML, como código para um evento de clique de botão.
Adicionar uma página "sobre"
A primeira personalização que você fará é adicionar outra página ao projeto. Esta página é uma página "sobre", que representa informações sobre esse aplicativo, como o autor, a versão e talvez um link para obter mais informações.
No painel Gerenciador de Soluções do Visual Studio, clique com o botão direito do mouse no projeto de Anotações >Adicionar>Novo Item....
Na caixa de diálogo Adicionar Novo Item, selecione .NET MAUI na lista de modelos no lado esquerdo da janela. Em seguida, selecione o modelo do .NET MAUI ContentPage (XAML). Nomeie o arquivo AboutPage.xaml e selecione Adicionar.
O arquivo AboutPage.xaml abrirá uma nova guia de documento, exibindo toda a marcação XAML que representa a interface do usuário da página. Substitua a marcação XAML pela marcação a seguir:
<?xml version="1.0" encoding="utf-8" ?> <ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="Notes.AboutPage"> <VerticalStackLayout Spacing="10" Margin="10"> <HorizontalStackLayout Spacing="10"> <Image Source="dotnet_bot.png" SemanticProperties.Description="The dot net bot waving hello!" HeightRequest="64" /> <Label FontSize="22" FontAttributes="Bold" Text="Notes" VerticalOptions="End" /> <Label FontSize="22" Text="v1.0" VerticalOptions="End" /> </HorizontalStackLayout> <Label Text="This app is written in XAML and C# with .NET MAUI." /> <Button Text="Learn more..." Clicked="LearnMore_Clicked" /> </VerticalStackLayout> </ContentPage>
Salve o arquivo pressionando CTRL+S ou selecionando o menu Arquivo>Salvar AboutPage.xaml.
Vamos especificar as partes principais dos controles XAML colocados na página:
<ContentPage>
é o objeto raiz da classeAboutPage
.<VerticalStackLayout>
é o único objeto filho do ContentPage. ContentPage só pode ter um objeto filho. O tipo VerticalStackLayout pode ter vários filhos. Esse controle de layout organiza seus filhos verticalmente, um após o outro.<HorizontalStackLayout>
opera da mesma forma que um<VerticalStackLayout>
, exceto que seus filhos são organizados horizontalmente.<Image>
exibe uma imagem, nesse caso, ela está usando a imagemdotnet_bot.png
que vem com todos os projetos do .NET MAUI.Importante
O arquivo adicionado ao projeto é, na verdade,
dotnet_bot.svg
. O .NET MAUI converte arquivos SVG (Gráficos vetoriais escalonáveis) em arquivos PNG (Portable Network Graphic) com base no dispositivo de destino. Portanto, ao adicionar um arquivo SVG ao seu projeto de aplicativo .NET MAUI, ele deve ser referenciado de XAML ou C# com uma extensão.png
. A única referência ao arquivo SVG deve estar no arquivo de projeto.<Label>
controla o texto de exibição.<Button>
controles podem ser pressionados pelo usuário, o que gera o eventoClicked
. Você pode executar o código em resposta ao eventoClicked
.Clicked="LearnMore_Clicked"
O evento
Clicked
do botão é atribuído ao manipulador de eventosLearnMore_Clicked
, que será definido no arquivo code-behind. Você criará esse código na próxima etapa.
Manipular o evento Clicked
O próximo passo é adicionar o código do evento Clicked
do botão.
No painel Solution Explorer do Visual Studio, expanda o arquivo AboutPage.xaml para revelar seu arquivo code-behind AboutPage.xaml.cs. Em seguida, clique duas vezes no arquivo AboutPage.xaml.cs para abri-lo no editor de código.
Adicione o seguinte código de manipulador de eventos
LearnMore_Clicked
, que abre o navegador do sistema em um URL específico:private async void LearnMore_Clicked(object sender, EventArgs e) { // Navigate to the specified URL in the system browser. await Launcher.Default.OpenAsync("https://aka.ms/maui"); }
Observe que a palavra-chave
async
foi adicionada à declaração do método, que permite o uso da palavra-chaveawait
ao abrir o navegador do sistema.Salve o arquivo pressionando CTRL+S ou selecionando o menu Arquivo>Salvar AboutPage.xaml.cs.
Agora que o XAML e o code-behind do AboutPage
estão concluídos, você precisará exibi-lo no aplicativo.
Adicionar recursos de imagem
Alguns controles podem usar imagens, o que aprimora a forma como os usuários interagem com seu aplicativo. Nesta seção, você baixará duas imagens que usará em seu aplicativo, juntamente com duas imagens alternativas para uso com o iOS.
Baixe as seguintes imagens:
Ícone: Sobre
Essa imagem é usada como um ícone para a página sobre a qual você criou anteriormente.Ícone: Observações
Essa imagem é usada como um ícone para a página de anotações que você criará na próxima parte deste tutorial.
Depois de baixar as imagens, você pode movê-las com o Explorador de Arquivos para a pasta Recursos\Imagens do projeto. Qualquer arquivo nessa pasta é incluído automaticamente no projeto como um recurso MauiImage. Você também pode usar o Visual Studio para adicionar as imagens ao seu projeto. Se você mover as imagens manualmente, ignore o procedimento a seguir.
Importante
Não ignore o download das imagens específicas do iOS, elas são necessárias para concluir este tutorial.
Mover as imagens com o Visual Studio
No painel Gerenciador de Soluções do Visual Studio, expanda a pasta Recursos, que revelará a pasta Imagens.
Dica
Você pode usar o Explorador de Arquivos para arrastar e soltar as imagens diretamente no painel Gerenciador de Soluções, na parte superior da pasta Imagens. Isso move automaticamente os arquivos para a pasta e os inclui no projeto. Se você optar por arrastar e soltar os arquivos, ignore o restante deste procedimento.
Clique com o botão direito do mouse em Imagens e selecione Adicionar>Item Existente....
Navegue até a pasta que contém as imagens baixadas.
Altere o filtro para filtro de tipo de arquivo para os Arquivos de Imagem.
Mantenha pressionada a tecla CTRL e clique em cada uma das imagens que você baixou e pressione Adicionar
Modifique o aplicativo Shell
Conforme observado no início deste artigo, a classe define a AppShell
hierarquia visual de um aplicativo, a marcação XAML usada na criação da interface do usuário do aplicativo. Atualize o XAML para adicionar um controle TabBar:
Clique duas vezes no arquivo AppShell.xaml no painel Gerenciador de Soluções para abrir o editor XAML. Substitua a marcação XAML pelo código a seguir:
<?xml version="1.0" encoding="UTF-8" ?> <Shell x:Class="Notes.AppShell" xmlns="http://schemas.microsoft.com/dotnet/2021/maui" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:local="clr-namespace:Notes" Shell.FlyoutBehavior="Disabled"> <TabBar> <ShellContent Title="Notes" ContentTemplate="{DataTemplate local:MainPage}" Icon="{OnPlatform 'icon_notes.png', iOS='icon_notes_ios.png', MacCatalyst='icon_notes_ios.png'}" /> <ShellContent Title="About" ContentTemplate="{DataTemplate local:AboutPage}" Icon="{OnPlatform 'icon_about.png', iOS='icon_about_ios.png', MacCatalyst='icon_about_ios.png'}" /> </TabBar> </Shell>
Salve o arquivo pressionando CTRL+S ou selecionando o menu Arquivo>Salvar AppShell.xaml.
Vamos dividir as partes principais do XAML:
<Shell>
é o objeto raiz da marcação XAML.<TabBar>
é o conteúdo do Shell.- Dois objetos
<ShellContent>
dentro do<TabBar>
. Antes de substituir o código do modelo, havia um único objeto<ShellContent>
, apontando para a páginaMainPage
.
O TabBar
e seus filhos não representam elementos de interface do usuário, mas sim a organização da hierarquia visual do aplicativo. O Shell usa esses objetos e produz a interface do usuário para o conteúdo, com uma barra na parte superior representando cada página. A propriedade ShellContent.Icon
para cada página usa sintaxe especial: {OnPlatform ...}
. Essa sintaxe é processada quando as páginas XAML são compiladas para cada plataforma e, com ela, você pode especificar um valor de propriedade para cada plataforma. Nesse caso, cada plataforma usa o ícone icon_about.png
por padrão, mas o iOS e o MacCatalyst usarão icon_about_ios.png
.
Cada objeto <ShellContent>
está apontando para uma página a ser exibida. Isso é definido pela propriedade ContentTemplate
.
Executar o aplicativo
Execute o aplicativo pressionando F5 ou pressionando o botão reproduzir na parte superior do Visual Studio:
Você verá que há duas guias: Anotações e sobre. Pressione a guia Sobre e o aplicativo navega até o AboutPage
que você criou. Pressione o botão Saiba mais... para abrir o navegador da Web.
Feche o aplicativo e retorne ao Visual Studio. Se você estiver usando o emulador do Android, encerre o aplicativo no dispositivo virtual ou pressione o botão parar na parte superior do Visual Studio:
Criar uma página para uma anotação
Agora que o aplicativo contém o MainPage
e AboutPage
, você pode começar a criar o restante do aplicativo. Primeiro, você criará uma página que permite que um usuário crie e exiba a anotação e, em seguida, você escreverá o código para carregar e salvar a nota.
A página de anotação exibirá a nota e permitirá que você a salve ou exclua. Primeiro, adicione a nova página ao projeto:
No painel Gerenciador de Soluções do Visual Studio, clique com o botão direito do mouse no projeto de Anotações >Adicionar>Novo Item....
Na caixa de diálogo Adicionar Novo Item, selecione .NET MAUI na lista de modelos no lado esquerdo da janela. Em seguida, selecione o modelo do .NET MAUI ContentPage (XAML). Nomeie o arquivo NotePage.xaml e selecione Adicionar.
O arquivo NotePage.xaml será aberto em uma nova guia, exibindo toda a marcação XAML que representa a interface do usuário da página. Substitua a marcação de código XAML na seguinte marcação:
<?xml version="1.0" encoding="utf-8" ?> <ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="Notes.NotePage" Title="Note"> <VerticalStackLayout Spacing="10" Margin="5"> <Editor x:Name="TextEditor" Placeholder="Enter your note" HeightRequest="100" /> <Grid ColumnDefinitions="*,*" ColumnSpacing="4"> <Button Text="Save" Clicked="SaveButton_Clicked" /> <Button Grid.Column="1" Text="Delete" Clicked="DeleteButton_Clicked" /> </Grid> </VerticalStackLayout> </ContentPage>
Salve o arquivo pressionando CTRL + S ou selecionando o menu Salvar>Salvar NotePage.xaml.
Vamos especificar as partes principais dos controles XAML colocados na página:
<VerticalStackLayout>
organiza seus controles filhos verticalmente, um abaixo do outro.<Editor>
é um controle de editor de texto de várias linhas e é o primeiro controle dentro de VerticalStackLayout.<Grid>
é um controle de layout e é o segundo controle dentro de VerticalStackLayout.Esse controle define colunas e linhas para criar células. Os controles filho são colocados dentro dessas células.
Por padrão, o controle Grid contém uma única linha e coluna, criando uma única célula. As colunas são definidas com uma largura e o valor
*
para largura informa à coluna para preencher o máximo de espaço possível. O snippet de código anterior definiu duas colunas, ambas usando o máximo de espaço possível, que distribui uniformemente as colunas no espaço alocado:ColumnDefinitions="*,*"
. Os tamanhos de coluna são separados por um caractere,
.Colunas e linhas definidas por um Grid são indexadas a partir de 0. Portanto, a primeira coluna seria o índice 0, a segunda coluna é o índice 1 e assim por diante.
Dois controles
<Button>
estão dentro do<Grid>
e atribuídos a uma coluna. Se um controle filho não definir uma atribuição de coluna, ele será atribuído automaticamente à primeira coluna. Nesta marcação, o primeiro botão é o botão "Salvar" e atribuído automaticamente à primeira coluna, coluna 0. O segundo botão é o botão "Excluir" e atribuído à segunda coluna, coluna 1.Observe que os dois botões têm o evento
Clicked
manipulado. Você adicionará o código para esses manipuladores na próxima seção.
Carregar e salvar uma anotação
Abra o arquivo NotePage.xaml.cs code-behind. Você pode abrir o code-behind para o arquivo NotePage.xaml de três maneiras:
- Se o NotePage.xaml estiver aberto e for o documento ativo que está sendo editado, pressione F7.
- Se o NotePage.xaml estiver aberto e for o documento ativo que está sendo editado, clique com o botão direito do mouse no editor de texto e selecione Exibir Código.
- Use o Gerenciador de Soluções para expandir a entrada NotePage.xaml, revelando o arquivo NotePage.xaml.cs. Clique duas vezes no arquivo para abri-lo.
Quando você adiciona um novo arquivo XAML, o code-behind contém uma única linha no construtor, uma chamada ao método InitializeComponent
:
namespace Notes;
public partial class NotePage : ContentPage
{
public NotePage()
{
InitializeComponent();
}
}
O método InitializeComponent
lê a marcação XAML e inicializa todos os objetos definidos pela marcação. Os objetos são conectados em suas relações pai-filho e os manipuladores de eventos definidos no código são anexados a eventos definidos no XAML.
Agora que você entende um pouco mais sobre arquivos code-behind, você adicionará código ao arquivo NotePage.xaml.cs code-behind para lidar com o carregamento e salvamento de anotações.
Quando uma anotação é criada, ela é salva no dispositivo como um arquivo de texto. O nome do arquivo é representado pela variável
_fileName
. Adicione a seguinte declaração de variávelstring
à classeNotePage
:public partial class NotePage : ContentPage { string _fileName = Path.Combine(FileSystem.AppDataDirectory, "notes.txt");
O código acima constrói um caminho para o arquivo, armazenando-o no diretório de dados local do aplicativo. O nome do arquivo é notes.txt.
No construtor da classe, depois que o método
InitializeComponent
for chamado, leia o arquivo do dispositivo e armazene seu conteúdo na propriedadeText
do controleTextEditor
:public NotePage() { InitializeComponent(); if (File.Exists(_fileName)) TextEditor.Text = File.ReadAllText(_fileName); }
Em seguida, adicione o código para lidar com os eventos de
Clicked
definidos no XAML:private void SaveButton_Clicked(object sender, EventArgs e) { // Save the file. File.WriteAllText(_fileName, TextEditor.Text); } private void DeleteButton_Clicked(object sender, EventArgs e) { // Delete the file. if (File.Exists(_fileName)) File.Delete(_fileName); TextEditor.Text = string.Empty; }
O método
SaveButton_Clicked
grava o texto no controle Editor, no arquivo representado pela variável_fileName
.O método
DeleteButton_Clicked
primeiro verifica se o arquivo representado pela variável_fileName
e, se existir, o exclui. Em seguida, o texto do controle Editor é limpo.Salve o arquivo pressionando CTRL + S ou selecionando o menu Salvar>Salvar NotePage.xaml.cs.
O código final do arquivo code-behind deve ser semelhante ao seguinte:
namespace Notes;
public partial class NotePage : ContentPage
{
string _fileName = Path.Combine(FileSystem.AppDataDirectory, "notes.txt");
public NotePage()
{
InitializeComponent();
if (File.Exists(_fileName))
TextEditor.Text = File.ReadAllText(_fileName);
}
private void SaveButton_Clicked(object sender, EventArgs e)
{
// Save the file.
File.WriteAllText(_fileName, TextEditor.Text);
}
private void DeleteButton_Clicked(object sender, EventArgs e)
{
// Delete the file.
if (File.Exists(_fileName))
File.Delete(_fileName);
TextEditor.Text = string.Empty;
}
}
Testar a anotação
Agora que página de notas está concluída, você precisa de uma maneira de apresentá-la ao usuário. Abra o arquivo AppShell.xaml e altere a primeira entrada ShellContent para apontar para o NotePage
em vez de MainPage
:
<?xml version="1.0" encoding="UTF-8" ?>
<Shell
x:Class="Notes.AppShell"
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:Notes"
Shell.FlyoutBehavior="Disabled">
<TabBar>
<ShellContent
Title="Notes"
ContentTemplate="{DataTemplate local:NotePage}"
Icon="{OnPlatform 'icon_notes.png', iOS='icon_notes_ios.png', MacCatalyst='icon_notes_ios.png'}" />
<ShellContent
Title="About"
ContentTemplate="{DataTemplate local:AboutPage}"
Icon="{OnPlatform 'icon_about.png', iOS='icon_about_ios.png', MacCatalyst='icon_about_ios.png'}" />
</TabBar>
</Shell>
Salve o arquivo e execute o aplicativo. Tente digitar na caixa de entrada e pressione o botão Salvar. Feche o aplicativo e reabra-o. A anotação inserida deve ser carregada do armazenamento do dispositivo.
Associar dados à interface do usuário e navegar em páginas
Esta parte do tutorial apresenta os conceitos de exibições, modelos e navegação no aplicativo.
Nas etapas anteriores do tutorial, você adicionou duas páginas ao projeto: NotePage
e AboutPage
. As páginas representam uma exibição de dados. O NotePage
é uma "exibição" que exibe "dados de anotação" e o AboutPage
é uma "exibição" que exibe "dados de informações do aplicativo". Ambas as exibições têm um modelo desses dados codificados ou inseridos neles, e você precisará separar o modelo de dados da exibição.
Qual é o benefício de separar o modelo da exibição? Ele permite que você projete a exibição para representar e interagir com qualquer parte do modelo sem se preocupar com o código real que implementa o modelo. Isso é feito usando a associação de dados, algo que será apresentado posteriormente neste tutorial. Por enquanto, porém, vamos reestruturar o projeto.
Separar a exibição e o modelo
Refatore o código existente para separar o modelo da exibição. As próximas etapas organizarão o código para que os modos de exibição e os modelos sejam definidos separadamente entre si.
Exclua MainPage.xaml e MainPage.xaml.cs do seu projeto, eles não são mais necessários. No painel Gerenciador de Soluções, localize a entrada para MainPage.xaml, clique com o botão direito do mouse e selecione Excluir.
Dica
Excluir o item MainPage.xaml também deve excluir o item MainPage.xaml.cs. Se MainPage.xaml.cs não tiver sido excluído, clique com o botão direito do mouse nele e selecione Excluir.
Clique com o botão direito do mouse no projeto Notes e selecione Adicionar>Nova Pasta. Nomeie a pasta Models.
Clique com o botão direito do mouse no projeto Notes e selecione Adicionar>Nova Pasta. Nomeie a pasta Views.
Localize o item NotePage.xaml e arraste-o para a pasta Views. O NotePage.xaml.cs deve se mover com ele.
Importante
Quando você move um arquivo, o Visual Studio geralmente solicita um aviso sobre como a operação de movimentação pode levar muito tempo. Isso não deve ser um problema aqui, pressione OK se você vir este aviso.
O Visual Studio também pode perguntar se você deseja ajustar o namespace do arquivo movido. Selecione Não, pois as próximas etapas alterarão o namespace.
Localize o item AboutPage.xaml e arraste-o para a pasta Views. O AboutPage.xaml.cs deve se mover com ele.
Atualizar o namespace de exibição
Agora que as exibições foram movidas para a pasta Views, você precisará atualizar os namespaces para corresponder. O namespace para os arquivos XAML e code-behind das páginas é definido como Notes
. Isso precisa ser atualizado para Notes.Views
.
No painel Gerenciador de Soluções, expanda NotePage.xaml e AboutPage.xaml para revelar os arquivos code-behind:
Clique duas vezes no item NotePage.xaml.cs para abrir o editor de código. Altere o namespace para
Notes.Views
:namespace Notes.Views;
Repita as etapas anteriores do item AboutPage.xaml.cs.
Clique duas vezes no item NotePage.xaml para abrir o editor XAML. O namespace antigo é referenciado por meio do atributo
x:Class
, que define qual tipo de classe é o code-behind do XAML. Essa entrada não é apenas o namespace, mas o namespace com o tipo. Altere o valorx:Class
paraNotes.Views.NotePage
:x:Class="Notes.Views.NotePage"
Repita a etapa anterior do item AboutPage.xaml, mas defina o valor
x:Class
comoNotes.Views.AboutPage
.
Corrigir a referência de namespace no Shell
O AppShell.xaml define duas guias, uma para o NotesPage
e outra para AboutPage
. Agora que essas duas páginas foram movidas para um novo namespace, o mapeamento de tipo no XAML agora é inválido. No painel Gerenciador de Soluções, clique duas vezes na entrada AppShell.xaml para abri-la no editor XAML. Ele deve se parecer com o seguinte snippet:
<?xml version="1.0" encoding="UTF-8" ?>
<Shell
x:Class="Notes.AppShell"
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:Notes"
Shell.FlyoutBehavior="Disabled">
<TabBar>
<ShellContent
Title="Notes"
ContentTemplate="{DataTemplate local:NotePage}"
Icon="{OnPlatform 'icon_notes.png', iOS='icon_notes_ios.png', MacCatalyst='icon_notes_ios.png'}" />
<ShellContent
Title="About"
ContentTemplate="{DataTemplate local:AboutPage}"
Icon="{OnPlatform 'icon_about.png', iOS='icon_about_ios.png', MacCatalyst='icon_about_ios.png'}" />
</TabBar>
</Shell>
Um namespace .NET é importado para o XAML por meio de uma declaração de namespace XML. Na marcação XAML anterior, é o atributo xmlns:local="clr-namespace:Notes"
no elemento raiz: <Shell>
. O formato de declarar um namespace XML para importar um namespace do .NET no mesmo assembly é:
xmlns:{XML namespace name}="clr-namespace:{.NET namespace}"
Portanto, a declaração anterior mapeia o namespace XML de local
para o namespace .NET de Notes
. É uma prática comum mapear o nome local
para o namespace raiz do projeto.
Remova o namespace local
XML e adicione um novo. Esse novo namespace XML será mapeado para o namespace do .NET de Notes.Views
; portanto, nomeie-o views
. A declaração deve ser semelhante ao seguinte atributo: xmlns:views="clr-namespace:Notes.Views"
.
O namespace XML local
foi usado pelas propriedades ShellContent.ContentTemplate
, altere-os para views
. Seu XAML agora deve se parecer com o seguinte snippet:
<?xml version="1.0" encoding="UTF-8" ?>
<Shell
x:Class="Notes.AppShell"
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:views="clr-namespace:Notes.Views"
Shell.FlyoutBehavior="Disabled">
<TabBar>
<ShellContent
Title="Notes"
ContentTemplate="{DataTemplate views:NotePage}"
Icon="{OnPlatform 'icon_notes.png', iOS='icon_notes_ios.png', MacCatalyst='icon_notes_ios.png'}" />
<ShellContent
Title="About"
ContentTemplate="{DataTemplate views:AboutPage}"
Icon="{OnPlatform 'icon_about.png', iOS='icon_about_ios.png', MacCatalyst='icon_about_ios.png'}" />
</TabBar>
</Shell>
Agora você deve ser capaz de executar o aplicativo sem erros do compilador e tudo ainda deve funcionar como antes.
Definir o modelo
Atualmente, o modelo são os dados inseridos na anotação e sobre exibições. Criaremos novas classes para representar esses dados. Primeiro, o modelo para representar os dados de uma página de anotação:
No painel Gerenciador de Soluções, clique com o botão direito do mouse na pasta Models e selecione Adicionar>Classe....
Nomeie a classe Note.cs e pressione Adicionar.
Abra Note.cs e substitua o código pelo seguinte snippet:
namespace Notes.Models; internal class Note { public string Filename { get; set; } public string Text { get; set; } public DateTime Date { get; set; } }
Salve o arquivo.
Em seguida, crie o modelo da página sobre:
No painel Gerenciador de Soluções, clique com o botão direito do mouse na pasta Models e selecione Adicionar>Classe....
Nomeie a classe About.cs e pressione Adicionar.
Abra About.cs e substitua o código pelo seguinte snippet:
namespace Notes.Models; internal class About { public string Title => AppInfo.Name; public string Version => AppInfo.VersionString; public string MoreInfoUrl => "https://aka.ms/maui"; public string Message => "This app is written in XAML and C# with .NET MAUI."; }
Salve o arquivo.
Página Atualizar Sobre
A página sobre será a página mais rápida a ser atualizada e você poderá executar o aplicativo e ver como ele carrega dados do modelo.
No painel Gerenciador de Soluções, abra o arquivo Views\AboutPage.xaml.
Substitua o conteúdo pelo seguinte snippet:
<?xml version="1.0" encoding="utf-8" ?> <ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:models="clr-namespace:Notes.Models" x:Class="Notes.Views.AboutPage"> <ContentPage.BindingContext> <models:About /> </ContentPage.BindingContext> <VerticalStackLayout Spacing="10" Margin="10"> <HorizontalStackLayout Spacing="10"> <Image Source="dotnet_bot.png" SemanticProperties.Description="The dot net bot waving hello!" HeightRequest="64" /> <Label FontSize="22" FontAttributes="Bold" Text="{Binding Title}" VerticalOptions="End" /> <Label FontSize="22" Text="{Binding Version}" VerticalOptions="End" /> </HorizontalStackLayout> <Label Text="{Binding Message}" /> <Button Text="Learn more..." Clicked="LearnMore_Clicked" /> </VerticalStackLayout> </ContentPage>
Vamos examinar as linhas alteradas, que são realçadas no snippet anterior:
xmlns:models="clr-namespace:Notes.Models"
Essa linha mapeia o namespace do .NET
Notes.Models
para o namespace XMLmodels
.A propriedade
BindingContext
do ContentPage é definida como uma instância da classeNote.Models.About
, usando o namespace XML e o objeto demodels:About
. Isso foi definido usando sintaxe de elemento de propriedade em vez de um atributo XML.Importante
Até agora, as propriedades foram definidas usando um atributo XML. Isso funciona bem para valores simples, como uma propriedade
Label.FontSize
. Mas se o valor da propriedade for mais complexo, você deverá usar sintaxe do elemento de propriedade para criar o objeto. Considere o seguinte exemplo de criação de um rótulo com seu conjunto de propriedadesFontSize
:<Label FontSize="22" />
A mesma propriedade
FontSize
pode ser definida usando sintaxe do elemento de propriedade:<Label> <Label.FontSize> 22 </Label.FontSize> </Label>
Três controles
<Label>
tiveram seu valor de propriedadeText
alterado de uma cadeia de caracteres codificada para uma sintaxe de associação:{Binding PATH}
.A sintaxe
{Binding}
é processada em tempo de execução, permitindo que o valor retornado da associação seja dinâmico. A partePATH
de{Binding PATH}
é o caminho de propriedade ao qual associar. A propriedade vem do controle atualBindingContext
. Com o controle<Label>
,BindingContext
não está definido. O contexto é herdado do pai quando não é definido pelo controle, o que, nesse caso, o contexto de configuração do objeto pai é o objeto raiz: ContentPage.O objeto no
BindingContext
é uma instância do modeloAbout
. O caminho de associação de um dos rótulos associa a propriedadeLabel.Text
à propriedadeAbout.Title
.
A última alteração na página sobre é atualizar o clique do botão que abre uma página da Web. A URL foi codificada no code-behind, mas a URL deve vir do modelo que está na propriedade BindingContext
.
No painel Gerenciador de Soluções, abra o arquivo Views\AboutPage.xaml.cs.
Substitua o método
LearnMore_Clicked
pelo seguinte código:private async void LearnMore_Clicked(object sender, EventArgs e) { if (BindingContext is Models.About about) { // Navigate to the specified URL in the system browser. await Launcher.Default.OpenAsync(about.MoreInfoUrl); } }
Se você observar a linha realçada, o código verificará se o BindingContext
é um Models.About
tipo e, se for, o atribuirá à about
variável. A próxima linha dentro da instrução if
abre o navegador para a URL fornecida pela propriedade about.MoreInfoUrl
.
Execute o aplicativo e você verá que ele é executado exatamente da mesma forma que antes. Tente alterar os valores do modelo e veja como a interface do usuário e a URL abertas pelo navegador também são alteradas.
Página Atualizar Observação
A seção anterior vinculou o modo de exibição de página about ao modelo de about e agora você fará o mesmo, associando o modo de exibição note ao modelo note. No entanto, nesse caso, o modelo não será criado no XAML, mas será fornecido no code-behind nas próximas etapas.
No painel Gerenciador de Soluções, abra o arquivo Views\NotePage.xaml.
Altere o controle
<Editor>
adicionando a propriedadeText
. Associe a propriedade à propriedadeText
:<Editor ... Text="{Binding Text}"
:<?xml version="1.0" encoding="utf-8" ?> <ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="Notes.Views.NotePage" Title="Note"> <VerticalStackLayout Spacing="10" Margin="5"> <Editor x:Name="TextEditor" Placeholder="Enter your note" Text="{Binding Text}" HeightRequest="100" /> <Grid ColumnDefinitions="*,*" ColumnSpacing="4"> <Button Text="Save" Clicked="SaveButton_Clicked" /> <Button Grid.Column="1" Text="Delete" Clicked="DeleteButton_Clicked" /> </Grid> </VerticalStackLayout> </ContentPage>
As modificações para o code-behind são mais complicadas do que o XAML. O código atual está carregando o conteúdo do arquivo no construtor e definindo-o diretamente para a propriedade TextEditor.Text
. Aqui está a aparência do código atual:
public NotePage()
{
InitializeComponent();
if (File.Exists(_fileName))
TextEditor.Text = File.ReadAllText(_fileName);
}
Em vez de carregar a anotação no construtor, crie um novo método LoadNote
. Esse método fará o seguinte:
- Aceite um parâmetro de nome de arquivo.
- Crie um novo modelo de anotação e defina o nome do arquivo.
- Se o arquivo existir, carregue seu conteúdo no modelo.
- Se o arquivo existir, atualize o modelo com a data em que o arquivo foi criado.
- Defina o
BindingContext
da página como o modelo.
No painel Gerenciador de Soluções, abra o arquivo Views\NotePage.xaml.cs.
Adicione o seguinte método à classe :
private void LoadNote(string fileName) { Models.Note noteModel = new Models.Note(); noteModel.Filename = fileName; if (File.Exists(fileName)) { noteModel.Date = File.GetCreationTime(fileName); noteModel.Text = File.ReadAllText(fileName); } BindingContext = noteModel; }
Atualize o construtor de classe para chamar
LoadNote
. O nome do arquivo da anotação deve ser um nome gerado aleatoriamente para ser criado no diretório de dados local do aplicativo.public NotePage() { InitializeComponent(); string appDataPath = FileSystem.AppDataDirectory; string randomFileName = $"{Path.GetRandomFileName()}.notes.txt"; LoadNote(Path.Combine(appDataPath, randomFileName)); }
Adicionar uma exibição e um modelo que lista todas as anotações
Esta parte do tutorial adiciona a parte final do aplicativo, uma exibição que exibe todas as anotações criadas anteriormente.
Várias anotações e navegação
Atualmente, o modo de exibição de anotação exibe uma única anotação. Para exibir várias anotações, crie uma nova exibição e um modelo: AllNotes.
- No painel Gerenciador de Soluções, clique com o botão direito do mouse na pasta Views e selecione Adicionar>Novo Item....
- Na caixa de diálogo Adicionar Novo Item, selecione .NET MAUI na lista de modelos no lado esquerdo da janela. Em seguida, selecione o modelo do .NET MAUI ContentPage (XAML). Nomeie o arquivo AllNotesPage.xaml e selecione Adicionar.
- No painel Gerenciador de Soluções, clique com o botão direito do mouse na pasta Models e selecione Adicionar>Classe...
- Nomeie a classe AllNotes.cs e pressione Adicionar.
Codificar o modelo do AllNotes
O novo modelo representará os dados necessários para exibir várias anotações. Esses dados serão uma propriedade que representa uma coleção de anotações. A coleção será uma ObservableCollection
que é uma coleção especializada. Quando um controle que lista vários itens, como um ListView, é associado a um ObservableCollection
, os dois trabalham juntos para manter automaticamente a lista de itens em sincronia com a coleção. Se a lista adicionar um item, a coleção será atualizada. Se a coleção adicionar um item, o controle será atualizado automaticamente com um novo item.
No painel do Gerenciador de Soluções, abra o arquivo Models\AllNotes.cs.
Substitua o código pelo seguinte snippet:
using System.Collections.ObjectModel; namespace Notes.Models; internal class AllNotes { public ObservableCollection<Note> Notes { get; set; } = new ObservableCollection<Note>(); public AllNotes() => LoadNotes(); public void LoadNotes() { Notes.Clear(); // Get the folder where the notes are stored. string appDataPath = FileSystem.AppDataDirectory; // Use Linq extensions to load the *.notes.txt files. IEnumerable<Note> notes = Directory // Select the file names from the directory .EnumerateFiles(appDataPath, "*.notes.txt") // Each file name is used to create a new Note .Select(filename => new Note() { Filename = filename, Text = File.ReadAllText(filename), Date = File.GetLastWriteTime(filename) }) // With the final collection of notes, order them by date .OrderBy(note => note.Date); // Add each note into the ObservableCollection foreach (Note note in notes) Notes.Add(note); } }
O código anterior declara uma coleção, chamada Notes
, e usa o método LoadNotes
para carregar anotações do dispositivo. Esse método usa extensões LINQ para carregar, transformar e classificar os dados na coleção Notes
.
Projetar a página AllNotes
Em seguida, o modo de exibição precisa ser projetado para dar suporte ao modelo AllNotes.
No painel Gerenciador de Soluções, abra o arquivo Views\AllNotesPage.xaml.
Substitua o código pela seguinte marcação:
<?xml version="1.0" encoding="utf-8" ?> <ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="Notes.Views.AllNotesPage" Title="Your Notes"> <!-- Add an item to the toolbar --> <ContentPage.ToolbarItems> <ToolbarItem Text="Add" Clicked="Add_Clicked" IconImageSource="{FontImage Glyph='+', Color=Black, Size=22}" /> </ContentPage.ToolbarItems> <!-- Display notes in a list --> <CollectionView x:Name="notesCollection" ItemsSource="{Binding Notes}" Margin="20" SelectionMode="Single" SelectionChanged="notesCollection_SelectionChanged"> <!-- Designate how the collection of items are laid out --> <CollectionView.ItemsLayout> <LinearItemsLayout Orientation="Vertical" ItemSpacing="10" /> </CollectionView.ItemsLayout> <!-- Define the appearance of each item in the list --> <CollectionView.ItemTemplate> <DataTemplate> <StackLayout> <Label Text="{Binding Text}" FontSize="22"/> <Label Text="{Binding Date}" FontSize="14" TextColor="Silver"/> </StackLayout> </DataTemplate> </CollectionView.ItemTemplate> </CollectionView> </ContentPage>
O XAML anterior apresenta alguns novos conceitos:
A propriedade
ContentPage.ToolbarItems
contém umToolbarItem
. Os botões definidos aqui geralmente são exibidos na parte superior do aplicativo, ao longo do título da página. Dependendo da plataforma, porém, ela pode estar em uma posição diferente. Quando um desses botões é pressionado, o eventoClicked
é acionado, assim como um botão normal.A propriedade
ToolbarItem.IconImageSource
define o ícone a ser exibido no botão. O ícone pode ser qualquer recurso de imagem definido pelo projeto, no entanto, neste exemplo, umFontImage
é usado. UmFontImage
pode usar um único glifo de uma fonte como uma imagem.O controle CollectionView exibe uma coleção de itens e, nesse caso, está associado à propriedade
Notes
do modelo. A maneira como cada item é apresentado pelo modo de exibição de coleção é definida por meio das propriedadesCollectionView.ItemsLayout
eCollectionView.ItemTemplate
.Para cada item da coleção, o
CollectionView.ItemTemplate
gera o XAML declarado. OBindingContext
desse XAML torna-se o item de coleção em si, nesse caso, cada anotação individual. O modelo da anotação usa dois rótulos, que são associados às propriedadesText
eDate
da nota.O CollectionView manipula o evento
SelectionChanged
, que é gerado quando um item no modo de exibição de coleção é selecionado.
O code-behind para a exibição precisa ser gravado para carregar as anotações e manipular os eventos.
No painel Gerenciador de Soluções, abra o arquivo Views/AllNotesPage.xaml.cs.
Substitua o código pelo seguinte snippet:
namespace Notes.Views; public partial class AllNotesPage : ContentPage { public AllNotesPage() { InitializeComponent(); BindingContext = new Models.AllNotes(); } protected override void OnAppearing() { ((Models.AllNotes)BindingContext).LoadNotes(); } private async void Add_Clicked(object sender, EventArgs e) { await Shell.Current.GoToAsync(nameof(NotePage)); } private async void notesCollection_SelectionChanged(object sender, SelectionChangedEventArgs e) { if (e.CurrentSelection.Count != 0) { // Get the note model var note = (Models.Note)e.CurrentSelection[0]; // Should navigate to "NotePage?ItemId=path\on\device\XYZ.notes.txt" await Shell.Current.GoToAsync($"{nameof(NotePage)}?{nameof(NotePage.ItemId)}={note.Filename}"); // Unselect the UI notesCollection.SelectedItem = null; } } }
Esse código usa o construtor para definir o BindingContext
da página para o modelo.
O método OnAppearing
é substituído da classe base. Esse método é chamado automaticamente sempre que a página é mostrada, como quando a página é navegada. O código aqui informa ao modelo para carregar as anotações. Como o CollectionView no modo de exibição AllNotes está vinculado à propriedade AllNotes do modelo Notes
, que é um ObservableCollection
, sempre que as notas são carregadas, o CollectionView é atualizado automaticamente.
O manipulador de Add_Clicked
apresenta outro novo conceito, a navegação. Como o aplicativo está usando o Shell do .NET MAUI, você pode navegar até páginas chamando o método Shell.Current.GoToAsync
. Observe que o manipulador é declarado com a palavra-chave async
, que permite o uso da palavra-chave await
ao navegar. Esse manipulador navega até o NotePage
.
A última parte do código no snippet anterior é o manipulador de notesCollection_SelectionChanged
. Esse método usa o item selecionado no momento, um modelo Note e usa suas informações para navegar até o NotePage
. GoToAsync usa uma cadeia de caracteres de URI para navegação. Nesse caso, uma cadeia de caracteres é construída que usa um parâmetro de cadeia de caracteres de consulta para definir uma propriedade na página de destino. A cadeia de caracteres interpolada que representa o URI acaba parecendo semelhante à seguinte cadeia de caracteres:
NotePage?ItemId=path\on\device\XYZ.notes.txt
O parâmetro ItemId=
é definido como o nome do arquivo no dispositivo em que a anotação é armazenada.
O Visual Studio pode estar indicando que a propriedade NotePage.ItemId
não existe, o que não existe. A próxima etapa é modificar a Note exibição para carregar o modelo com base no parâmetro ItemId
que você criará.
Parâmetros de cadeia de caracteres de consulta
A Note exibição precisa dar suporte ao parâmetro de cadeia de caracteres de consulta, ItemId
. Crie-o agora:
No painel Gerenciador de Soluções, abra o arquivo Views/NotePage.xaml.cs.
Adicione o atributo
QueryProperty
à palavra-chaveclass
, fornecendo o nome da propriedade de cadeia de caracteres de consulta e a propriedade de classe para a qual ela mapeia,ItemId
eItemId
respectivamente:[QueryProperty(nameof(ItemId), nameof(ItemId))] public partial class NotePage : ContentPage
Adicione uma nova propriedade
string
chamadaItemId
. Essa propriedade chama o métodoLoadNote
, passando o valor da propriedade, que, por sua vez, deve ser o nome do arquivo da nota:public string ItemId { set { LoadNote(value); } }
Substitua os manipuladores
SaveButton_Clicked
eDeleteButton_Clicked
pelo seguinte código:private async void SaveButton_Clicked(object sender, EventArgs e) { if (BindingContext is Models.Note note) File.WriteAllText(note.Filename, TextEditor.Text); await Shell.Current.GoToAsync(".."); } private async void DeleteButton_Clicked(object sender, EventArgs e) { if (BindingContext is Models.Note note) { // Delete the file. if (File.Exists(note.Filename)) File.Delete(note.Filename); } await Shell.Current.GoToAsync(".."); }
Os botões agora são
async
. Depois que eles são pressionados, a página navega de volta para a página anterior usando um URI de..
.Exclua a variável
_fileName
da parte superior do código, pois ela não é mais usada pela classe.
Modificar a árvore visual do aplicativo
O AppShell
ainda está carregando a página de anotação única, em vez disso, ele precisa carregar a exibição AllPages. Abra o arquivo AppShell.xaml e altere a primeira entrada ShellContent para apontar para o AllNotesPage
em vez de NotePage
:
<?xml version="1.0" encoding="UTF-8" ?>
<Shell
x:Class="Notes.AppShell"
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:views="clr-namespace:Notes.Views"
Shell.FlyoutBehavior="Disabled">
<TabBar>
<ShellContent
Title="Notes"
ContentTemplate="{DataTemplate views:AllNotesPage}"
Icon="{OnPlatform 'icon_notes.png', iOS='icon_notes_ios.png', MacCatalyst='icon_notes_ios.png'}" />
<ShellContent
Title="About"
ContentTemplate="{DataTemplate views:AboutPage}"
Icon="{OnPlatform 'icon_about.png', iOS='icon_about_ios.png', MacCatalyst='icon_about_ios.png'}" />
</TabBar>
</Shell>
Se você executar o aplicativo agora, perceberá que ele falhará se pressionar o botão Adicionar, reclamando que ele não pode navegar até NotesPage
. Todas as páginas que podem ser navegadas de outra página precisam ser registradas no sistema de navegação. As páginas AllNotesPage
e AboutPage
são registradas automaticamente no sistema de navegação sendo declaradas no TabBar.
Registre o NotesPage
com o sistema de navegação:
No painel Gerenciador de Soluções, abra o arquivo AppShell.xaml.cs.
Adicione uma linha ao construtor que registra a rota de navegação:
namespace Notes; public partial class AppShell : Shell { public AppShell() { InitializeComponent(); Routing.RegisterRoute(nameof(Views.NotePage), typeof(Views.NotePage)); } }
O método Routing.RegisterRoute
usa dois parâmetros:
- O primeiro parâmetro é o nome da cadeia de caracteres do URI que você deseja registrar, nesse caso, o nome resolvido é
"NotePage"
. - O segundo parâmetro é o tipo de página a ser carregada quando
"NotePage"
é navegada.
Agora você pode executar seu aplicativo. Tente adicionar novas anotações, navegar entre anotações e excluir anotações.
Explore o código deste tutorial.. Se você quiser baixar uma cópia do projeto concluído com a qual comparar seu código, baixe este projeto.
Parabéns!
Você concluiu o tutorial Criar um aplicativo .NET MAUI!
Próximas etapas
Na próxima parte da série de tutoriais, você aprenderá a implementar padrões MVVM (model-view-viewmodel) em seu projeto.
Os seguintes links fornecem mais informações relacionadas a alguns dos conceitos que você aprendeu neste tutorial:
Tem algum problema com essa seção? Se tiver, envie seus comentários para que possamos melhorar esta seção.