Módulo 6 · Aplicações Desktop — Capítulo 06

Projeto Prático — App Desktop com SQL Server

O capítulo final do curso: uma aplicação WPF completa de cadastro e listagem de clientes, MVVM do início ao fim, sobre as mesmas procedures usadas em todos os módulos anteriores.

1. O que este projeto amarra

CapítuloPeça aplicada no projeto
01Projeto WPF criado com XAML + code-behind mínimo
02ViewModelBase com INotifyPropertyChanged
03Data binding de campos e RelayCommand para Salvar/Excluir
04INavegacaoService entre lista e cadastro, DI configurada em App.xaml.cs
05Chamadas assíncronas com indicador de Carregando, sem travar a janela

2. A tela de listagem

C# ClienteListaViewModel.cs
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;
        }
    }
}
XAML ClienteListaView.xaml
<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>
C# ClienteListaView.xaml.cs
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

C# App.xaml.cs — o projeto inteiro fiado junto
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.

📌 Resumo do capítulo

  • Uma aplicação WPF completa em MVVM reaproveita IClienteRepository exatamente como MVC e API — a arquitetura muda, o acesso a dados não.
  • O host genérico (Microsoft.Extensions.Hosting) unifica configuração e DI entre Desktop, Web e Worker Services.
  • Procedures bem desenhadas no Módulo 2 são o investimento que se paga em cada módulo de arquitetura construído por cima delas.

✏️ Praticando — projeto de encerramento

  1. Implemente a aplicação completa deste capítulo: lista, cadastro, navegação e indicador de carregamento.
  2. Adicione a tela de exclusão de cliente, reaproveitando usp_ClienteExcluir do Módulo 2.
  3. Como projeto final do curso: escolha um dos outros três módulos de arquitetura (MVC, API ou Worker Service) e implemente a mesma funcionalidade de clientes nele, comparando as diferenças e semelhanças de cada abordagem em primeira mão.