.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: