.NET
MAUI - Usando o banco de dados SQLite - II
![]() |
Neste artigo vamos criar uma aplicação .NET MAUI para gerenciar um pequena biblioteca de livros pessoal realizando a persistência dos dados no SQLite. |
Continuando a primeira parte do artigo vamos criar as views , fazer as vinculações e registro dos serviços e preparar nossa aplicação para ser executada.
Criando as Views
Vamos agora criar na pasta MVVM/Views as respectivas views definindo o leiaute da interface com o usuário e vinculando cada view à sua view model. Todas as views são do tipo Content Page.
1- LivrosPage.xaml
<?xml version="1.0" encoding="utf-8" ?> <ContentPage x:Class="MeusLivros.MVVM.Views.LivrosPage" xmlns="http://schemas.microsoft.com/dotnet/2021/maui" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:model="clr-namespace:MeusLivros.MVVM.Models" xmlns:toolkit="http://schemas.microsoft.com/dotnet/2022/maui/toolkit" xmlns:viewmodel="clr-namespace:MeusLivros.MVVM.ViewModels" Title="Meus Livros" x:DataType="viewmodel:LivrosViewModel"> <ContentPage.Resources> <toolkit:BoolToObjectConverter x:Key="BoolToObjectConverter" FalseObject="Leitura ❌" TrueObject="Concluída ✔" /> </ContentPage.Resources> <ContentPage.Behaviors> <toolkit:EventToCommandBehavior Command="{Binding GetLivrosCommand}" EventName="Appearing" /> </ContentPage.Behaviors> <Grid Margin="20" RowDefinitions="Auto,*" RowSpacing="10"> <Grid Grid.Row="0" ColumnDefinitions="*,*"> <SearchBar Grid.Column="0" Placeholder="Localizar Livro" TextChanged="SearchBar_TextChanged"/> <Button Grid.Column="1" Background="gold" Command="{Binding AddLivroCommand}" FontAttributes="Bold" HorizontalOptions="End" Text="Novo Livro" TextColor="Black" /> </Grid> <CollectionView x:Name="ColView1" Grid.Row="1" ItemsSource="{Binding MeusLivros}"> <CollectionView.ItemsLayout> <LinearItemsLayout ItemSpacing="4" Orientation="Vertical" /> </CollectionView.ItemsLayout> <CollectionView.ItemTemplate> <DataTemplate x:DataType="model:Livro"> <Border Padding="15" Background="lightgray" Stroke="Dimgray" StrokeThickness="2"> <Border.StrokeShape> <RoundRectangle CornerRadius="10,10,10,10" /> </Border.StrokeShape> <Grid RowDefinitions="Auto,*" RowSpacing="4"> <Image Grid.Row="0" Aspect="AspectFill" HeightRequest="200" HorizontalOptions="Center" Source="{Binding ImagemUrl}" VerticalOptions="Center" /> <Grid Grid.Row="1" RowDefinitions="Auto,Auto,Auto,Auto"> <Label Grid.Row="0" FontAttributes="Bold" FontSize="Medium" Text="{Binding Titulo}" /> <Label Grid.Row="1" Margin="0,2" FontSize="Small" Text="{Binding Autor}" /> <Label Grid.Row="2" Margin="0,2" FontSize="Small" Text="{Binding Paginas, StringFormat='{0:F0} páginas'}" /> <Grid Grid.Row="3" Margin="0,5" ColumnDefinitions="Auto,Auto,*"> <Button Grid.Column="0" BackgroundColor="#4A88DA" Command="{Binding Source={RelativeSource AncestorType={x:Type viewmodel:LivrosViewModel}}, Path=UpdateLivroCommand}" CommandParameter="{Binding .}" FontAttributes="Bold" Text="Atualizar" TextColor="#eff5f3" /> <Button Grid.Column="1" Margin="8,0" BackgroundColor="#F44336" Command="{Binding Source={RelativeSource AncestorType={x:Type viewmodel:LivrosViewModel}}, Path=DeleteLivroCommand}" CommandParameter="{Binding .}" FontAttributes="Bold" Text="Deletar" TextColor="#eff5f3" /> <Label Grid.Column="2" HorizontalOptions="End" LineBreakMode="TailTruncation" Text="{Binding LeituraConcluida, Converter={StaticResource BoolToObjectConverter}}" TextColor="Blue" VerticalOptions="Center" /> </Grid> </Grid> </Grid> </Border> </DataTemplate> </CollectionView.ItemTemplate> </CollectionView> </Grid> </ContentPage> |
Destaques do código :
x:DataType
é usado
para especificar o tipo de dados associado a esta página. Ele está vinculado
ao ViewModel LivrosViewModel
, o
que significa que o DataContext da página será definido como uma instância
de LivrosViewModel
.BoolToObjectConverter
. Este recurso é usado para
converter valores booleanos em texto ("Leitura ❌" ou "Concluída ✔") e é
usado posteriormente em um Label
. Para isso estamos usando o
pacote CommunityToolkit.Maui.ContentPage.Behaviors
define comportamentos
associados à página. Um comportamento
EventToCommandBehavior
está associado ao evento "Appearing"
da página. Ele está vinculado a um comando chamado
GetLivrosCommand
no ViewModel, que será acionado quando
a página aparecer.Neste código definidos nos botões de comando para
Atualizar e Deletar :
Command="{Binding Source={RelativeSource
AncestorType={x:Type viewmodel:LivrosViewModel}}, Path=UpdateLivroCommand}"
:
Este atributo associa um comando ao botão sendo que o comando é definido no
ViewModel e é acionado quando o botão é pressionado:Command="{Binding ...}"
: Isso
define o comando a ser acionado quando o botão for pressionado.Source={RelativeSource AncestorType={x:Type
viewmodel:LivrosViewModel}}
: Isso especifica que o comando
será procurado no ViewModel do tipo
LivrosViewModel
na hierarquia de elementos pai da página
atual.Path=UpdateLivroCommand
:
Especifica que o caminho para o comando é
UpdateLivroCommand
, que é um comando definido no
LivrosViewModel
.2- AddLivroPage.xaml
<?xml version="1.0" encoding="utf-8" ?> <ContentPage x:Class="MeusLivros.MVVM.Views.AddLivroPage" xmlns="http://schemas.microsoft.com/dotnet/2021/maui" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:viewmodel="clr-namespace:MeusLivros.MVVM.ViewModels" Title="Incluir Livro"> <VerticalStackLayout Margin="20"> <Entry Placeholder="Titulo" Text="{Binding LivroTitulo}" /> <Entry Placeholder="Autor" Text="{Binding LivroAutor}" /> <Entry Placeholder="Imagem" Text="{Binding LivroImagemUrl}" /> <Entry Placeholder="No. de Páginas" Text="{Binding LivroPaginas}" /> <Entry Placeholder="Emprestado para..." Text="{Binding LivroEmprestadoPara}" /> <HorizontalStackLayout> <CheckBox IsChecked="{Binding LivroLeituraConcluida}" /> <Label Text="Você concluiu a leitura deste livro ?" VerticalOptions="Center" /> </HorizontalStackLayout> <Button Background="gold" Command="{Binding AddLivroCommand}" FontAttributes="Bold" Text="Novo Livro" TextColor="Black" /> </VerticalStackLayout> </ContentPage> |
Vamos entender o código:
xmlns:viewmodel
: Este
atributo xmlns
define um namespace para o namespace de
ViewModels (MeusLivros.MVVM.ViewModels
).
Isso permite que você associe elementos da interface do usuário a propriedades e
comandos definidos nos ViewModels.
A seguir temos as seguintes vinculações:
Text="{Binding LivroTitulo}"
:
Isso liga o texto do Entry
à propriedade LivroTitulo
no ViewModel. Quando o usuário insere um título, ele é armazenado na
propriedade LivroTitulo
.IsChecked="{Binding LivroLeituraConcluida}"
:
Isso liga o estado do CheckBox
à propriedade
LivroLeituraConcluida
no ViewModel. Quando o usuário marca ou
desmarca o CheckBox
, a propriedade
LivroLeituraConcluida
reflete esse estado.Command="{Binding AddLivroCommand}"
:
Isso liga o comando do botão Button
ao comando
AddLivroCommand
no ViewModel. Quando o botão é pressionado, o comando
AddLivroCommand
é acionado no
ViewModel.3- UpdateLivroPage.xaml
<?xml version="1.0" encoding="utf-8" ?> <ContentPage x:Class="MeusLivros.MVVM.Views.UpdateLivroPage" xmlns="http://schemas.microsoft.com/dotnet/2021/maui" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:viewmodel="clr-namespace:MeusLivros.MVVM.ViewModels" Title="Atualizar Livro" x:DataType="viewmodel:UpdateLivroViewModel"> <VerticalStackLayout Margin="20"> <Entry Placeholder="Titulo" Text="{Binding Livro.Titulo}" /> <Entry Placeholder="Autor" Text="{Binding Livro.Autor}" /> <Entry Placeholder="Imagem" Text="{Binding Livro.ImagemUrl}" /> <Entry Placeholder="No. de Páginas" Text="{Binding Livro.Paginas}" /> <Entry Placeholder="Emprestado para..." Text="{Binding Livro.EmprestadoPara}" /> <HorizontalStackLayout> <CheckBox IsChecked="{Binding Livro.LeituraConcluida}" /> <Label Text="Já concluiu a leitura deste livro ?" VerticalOptions="Center" /> </HorizontalStackLayout> <Button Background="#4A88DA" Command="{Binding UpdateLivroCommand}" Text="Atualizar Livro" TextColor="#eff5f3" /> </VerticalStackLayout> </ContentPage> |
A lógica desta view é idêntica à anterior, a diferença é que aqui estamos vinculando a ação ao comando UpdateLivroCommand.
A seguir temos o código que faz a vinculação de cada view com a respectiva view model no arquivo code-behind:
1- LivrosPages.xaml.cs
public partial class LivrosPage : ContentPage { private readonly ILivroService _livroService; public LivrosPage(LivrosViewModel livrosViewModel, ILivroService livroService) { InitializeComponent(); BindingContext = livrosViewModel; _livroService = livroService; } private async void SearchBar_TextChanged(object sender, TextChangedEventArgs e) { var livros = await _livroService.GetLivroTituloAsync(((SearchBar)sender).Text); ColView1.ItemsSource = livros; } } |
2- AddLivroPage.xaml.cs
public partial class AddLivroPage : ContentPage { public AddLivroPage(AddLivroViewModel addLivroViewModel) { InitializeComponent(); BindingContext = addLivroViewModel; } } |
3- UpdateLivroPage.xaml.cs
public partial class UpdateLivroPage : ContentPage { public UpdateLivroPage(UpdateLivroViewModel updateLivroViewModel) { InitializeComponent(); BindingContext = updateLivroViewModel; } } |
Não podemos esquecer de registrar os serviços no contêiner DI na classe MauiProgram:
public static class MauiProgram { public static MauiApp CreateMauiApp() { var builder = MauiApp.CreateBuilder(); builder .UseMauiApp<App>() .UseMauiCommunityToolkit() .ConfigureFonts(fonts => { fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular"); fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold"); }); #if DEBUG builder.Logging.AddDebug(); #endif // ViewModels builder.Services.AddSingleton<LivrosViewModel>(); builder.Services.AddTransient<AddLivroViewModel>(); builder.Services.AddTransient<UpdateLivroViewModel>(); // Views builder.Services.AddSingleton<LivrosPage>(); builder.Services.AddTransient<AddLivroPage>(); builder.Services.AddTransient<UpdateLivroPage>(); // Service builder.Services.AddSingleton<ILivroService, LivroService>(); return builder.Build(); } } |
Neste código destacamos o seguinte:
.UseMauiCommunityToolkit()
:
Este método configura o aplicativo para usar o community toolkit maui(CommunityToolkit)
que fornece funcionalidades e controles adicionais para a construção de
aplicativos.
builder.Services.AddSingleton<LivrosViewModel>();
: Aqui, o
construtor está configurando a injeção de dependência para o ViewModel
LivrosViewModel
, registrando-o como um serviço Singleton. Isso significa
que uma única instância do LivrosViewModel
será compartilhada em
todo o aplicativo.
builder.Services.AddTransient<AddLivroViewModel>();
e
builder.Services.AddTransient<UpdateLivroViewModel>();
: Da
mesma forma, os ViewModels AddLivroViewModel
e
UpdateLivroViewModel
são configurados como serviços transientes. Isso
significa que uma nova instância desses ViewModels será criada sempre que for
solicitada.builder.Services.AddSingleton<LivrosPage>();
,
builder.Services.AddTransient<AddLivroPage>();
, e
builder.Services.AddTransient<UpdateLivroPage>();
:
Os tipos de página correspondentes aos ViewModels também são configurados como
serviços. LivrosPage
é registrado como
Singleton, enquanto
AddLivroPage
e UpdateLivroPage
são registrados como
transientes.builder.Services.AddSingleton<ILivroService,
LivroService>();
: Aqui, o serviço
ILivroService
é configurado para usar a implementação
LivroService
como implementação
concreta. Isso permite que outros componentes do aplicativo dependam de
ILivroService
e usem a implementação
LivroService
para acessar
funcionalidades relacionadas a livros.Tipos de vida usados nos serviços:
LivrosPage
é a página
principal do aplicativo que exibe uma lista de livros. Ela pode ser
usada em toda a vida do aplicativo, e é comum manter as páginas
principais como instâncias Singleton, pois
elas geralmente não mudam ao longo da sessão do usuário.ILivroService é o
serviço
responsável por interagir com a camada de dados, neste caso,
ILivroService
, é frequentemente registrado como
Singleton para garantir que haja uma única
instância compartilhada em todo o aplicativo. Isso é útil para manter um
único estado de conexão com o banco de dados ou gerenciar recursos
compartilhados, economizando recursos e garantindo consistência em toda
a aplicação.AddLivroViewModel
e
UpdateLivroViewModel
são ViewModels usadas para controlar
as operações de adição e atualização de livros. Essas ViewModels podem
ser usadas temporariamente durante a adição ou atualização de um
livro específico e não precisam ser compartilhados entre várias
partes do aplicativo. Portanto, eles são registrados como
Transient, o que significa que uma nova
instância será criada sempre que necessário e será descartada quando não
for mais usada. Isso evita o acúmulo de estado desnecessário.Agora vamos configurar as rotas usadas no projeto no arquivo AppShell.xaml e AppShell.xaml.cs .
1- AppShell.xaml
<?xml version="1.0" encoding="UTF-8" ?> <Shell x:Class="MeusLivros.AppShell" xmlns="http://schemas.microsoft.com/dotnet/2021/maui" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:local="clr-namespace:MeusLivros" xmlns:views="clr-namespace:MeusLivros.MVVM.Views" Shell.FlyoutBehavior="Disabled"> <ShellContent ContentTemplate="{DataTemplate views:LivrosPage}" Route="LivrosPage" /> </Shell> |
Este código define a estrutura básica do Shell do aplicativo .NET MAUI,
desativando o menu de navegação lateral (flyout) e
configurando LivrosPage
como a página
principal exibida quando o aplicativo é iniciado.
<ShellContent>
: Este é um
elemento dentro do Shell que define o conteúdo principal da página. Ele está
associado a um modelo de conteúdo (ContentTemplate)
que especifica qual página será exibida como conteúdo principal. Neste caso, a
página LivrosPage
será exibida como
conteúdo principal quando o aplicativo for iniciado. O atributo
Route
especifica o rótulo ou
identificador da página.
2- AppShell.xaml.cs
public partial class AppShell : Shell { public AppShell() { InitializeComponent(); RegisterForRoute<AddLivroPage>(); RegisterForRoute<UpdateLivroPage>(); } protected void RegisterForRoute<T>() { Routing.RegisterRoute(typeof(T).Name, typeof(T)); } } |
Vamos entender o código usado:
InitializeComponent()
:
Este método é gerado automaticamente e é usado para inicializar os
componentes do layout XAML associados à classe AppShell
. Essa
chamada garante que o layout definido no arquivo XAML seja carregado e
configurado corretamente.RegisterForRoute<AddLivroPage>();
e RegisterForRoute<UpdateLivroPage>();
: Esses
métodos são chamados no construtor para registrar páginas específicas que
podem ser navegadas usando rotas no aplicativo. Eles usam o método
Routing.RegisterRoute
para
associar o nome da classe da página (AddLivroPage
e UpdateLivroPage
) com a classe real que representa a
página.protected void RegisterForRoute<T>()
:
Este é um método genérico que permite registrar páginas para navegação com
base no tipo de página. Ele usa reflection para
obter o nome da classe do tipo T
e, em seguida, registra uma
rota com esse nome.Com isso temos tudo pronto para por o nosso projeto para funcionar.
Executando o projeto em um emulador Android teremos o seguinte resultado:
1- A tela inicial
2- Incluindo um novo livro
3- Atualizando um livro
4- Excluindo um livro
Pegue o código do projeto aqui:
MeusLivrosApp.zip
"['Senhor'] O teu reino é um reino eterno; o teu domínio dura em todas
as gerações."
Salmos
145:13
Referências: