1. O que este projeto amarra
| Capítulo | Peça aplicada no projeto |
|---|---|
| 01 | Projeto WPF criado com XAML + code-behind mínimo |
| 02 | ViewModelBase com INotifyPropertyChanged |
| 03 | Data binding de campos e RelayCommand para Salvar/Excluir |
| 04 | INavegacaoService entre lista e cadastro, DI configurada em App.xaml.cs |
| 05 | Chamadas assíncronas com indicador de Carregando, sem travar a janela |
2. A tela de listagem
public class ClienteListaViewModel : ViewModelBase
{
private readonly IClienteRepository _repositorio;
private readonly INavegacaoService _navegacao;
public ObservableCollection<Cliente> Clientes { get; } = new();
private bool _carregando;
public bool Carregando { get => _carregando; set => DefinirCampo(ref _carregando, value); }
public ICommand CarregarCommand { get; }
public ICommand NovoClienteCommand { get; }
public ICommand AbrirDetalhesCommand { get; }
public ClienteListaViewModel(IClienteRepository repositorio, INavegacaoService navegacao)
{
_repositorio = repositorio;
_navegacao = navegacao;
CarregarCommand = new RelayCommand(async _ => await CarregarAsync());
NovoClienteCommand = new RelayCommand(_ => _navegacao.AbrirTelaCadastroCliente());
AbrirDetalhesCommand = new RelayCommand(p => _navegacao.AbrirTelaDetalhesCliente((int)p!));
}
private async Task CarregarAsync()
{
Carregando = true;
try
{
Clientes.Clear();
var lista = await _repositorio.ListarAsync(nome: null, ativo: true);
foreach (var cliente in lista)
Clientes.Add(cliente);
}
finally
{
Carregando = false;
}
}
}
<Window x:Class="AppClientes.ClienteListaView" Title="Clientes" Height="500" Width="700"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Loaded="Window_Loaded">
<DockPanel Margin="15">
<StackPanel DockPanel.Dock="Top" Orientation="Horizontal" Margin="0,0,0,10">
<Button Content="Novo Cliente" Command="{Binding NovoClienteCommand}" />
<ProgressBar Width="100" IsIndeterminate="True" Margin="10,0,0,0"
Visibility="{Binding Carregando, Converter={StaticResource BoolParaVisibilidade}}" />
</StackPanel>
<ListView ItemsSource="{Binding Clientes}">
<ListView.View>
<GridView>
<GridViewColumn Header="Nome" DisplayMemberBinding="{Binding Nome}" />
<GridViewColumn Header="Email" DisplayMemberBinding="{Binding Email}" />
</GridView>
</ListView.View>
</ListView>
</DockPanel>
</Window>
public partial class ClienteListaView : Window
{
public ClienteListaView(ClienteListaViewModel viewModel)
{
InitializeComponent();
DataContext = viewModel;
}
private async void Window_Loaded(object sender, RoutedEventArgs e)
=> await ((ClienteListaViewModel)DataContext).CarregarCommand.Execute(null).AsTask();
}
3. Registro completo no App.xaml.cs
public partial class App : Application
{
private readonly IHost _host;
public App()
{
_host = Host.CreateDefaultBuilder()
.ConfigureAppConfiguration(config => config.AddJsonFile("appsettings.json"))
.ConfigureServices((contexto, servicos) =>
{
var connectionString = contexto.Configuration.GetConnectionString("Default")!;
servicos.AddSingleton<INavegacaoService, NavegacaoService>();
servicos.AddSingleton<SessaoUsuario>();
servicos.AddScoped<IClienteRepository>(_ => new ClienteRepository(connectionString));
servicos.AddTransient<ClienteListaViewModel>();
servicos.AddTransient<ClienteCadastroViewModel>();
servicos.AddTransient<ClienteListaView>();
servicos.AddTransient<ClienteCadastroView>();
})
.Build();
}
protected override async void OnStartup(StartupEventArgs e)
{
await _host.StartAsync();
_host.Services.GetRequiredService<ClienteListaView>().Show();
base.OnStartup(e);
}
protected override async void OnExit(ExitEventArgs e)
{
await _host.StopAsync();
_host.Dispose();
base.OnExit(e);
}
}
4. Todo o percurso, de ponta a ponta
Vale parar um momento e observar o que esse pequeno projeto representa: a mesma tabela
Cliente e as mesmas procedures (usp_ClienteListar,
usp_ClienteInserir) desenhadas lá no Módulo 2 são consumidas, sem
nenhuma alteração, por três interfaces completamente diferentes construídas ao longo deste curso:
uma tela web MVC (Módulo 3), um endpoint de API JSON (Módulo 4) e agora uma janela desktop
(Módulo 6) — todas passando pelo mesmo IClienteRepository. Essa é a
prova prática de por que investir em Stored Procedures bem desenhadas e numa camada de acesso a
dados desacoplada (Módulo 1, capítulo 12) compensa: a lógica de negócio no banco não precisa ser
reescrita a cada novo tipo de aplicação construída em cima dela.
5. Fim do curso — e o que fazer a partir daqui
Este material cobriu C# do zero ao avançado, SQL Server com foco pesado em Stored Procedures, e quatro arquiteturas completas construídas sobre essa base: MVC, APIs, Worker Services e aplicações Desktop. O próximo passo natural é combinar os módulos num único sistema maior — por exemplo, a mesma API do Módulo 4 servindo dados para um front-end web, enquanto um Worker Service (Módulo 5) processa pedidos em segundo plano e uma aplicação Desktop (este módulo) atende a operação interna da equipe, todos compartilhando as mesmas procedures do Módulo 2.