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

Acesso a Dados em Apps Desktop

Chamando as mesmas procedures do Módulo 2 a partir de um ViewModel — e o cuidado extra que uma interface gráfica exige: nunca travar a janela enquanto espera o banco.

1. O mesmo Repository, chamado de um lugar diferente

Não existe nada de especial em IClienteRepository quando usado numa aplicação desktop — é a mesma interface, a mesma implementação (ADO.NET, Dapper ou EF Core, Módulo 1 capítulos 13/15) usada em MVC e API. O que muda é quem chama: em vez de um Controller reagindo a uma requisição, é um ViewModel reagindo a um Command.

2. Nunca bloqueie a thread de UI

Toda aplicação com interface gráfica tem uma thread de UI dedicada a desenhar a tela e responder a cliques. Se essa thread ficar ocupada esperando uma chamada ao banco terminar, a janela inteira "congela" — não redesenha, não responde a cliques, o Windows chega a marcar como "Não está respondendo". async/await (Módulo 1, capítulo 10) existe exatamente para isso.

C# errado — trava a janela
private void SalvarCommand_Execute(object? parametro)
{
    _repositorio.InserirAsync(cliente).Wait(); // BLOQUEIA a thread de UI
    Mensagem = "Salvo!";
}
C# correto — UI continua responsiva
private async Task SalvarAsync()
{
    await _repositorio.InserirAsync(cliente); // libera a thread de UI durante a espera
    Mensagem = "Salvo!";
}
O mesmo aviso do Módulo 1, capítulo 10

.Wait()/.Result numa Task ainda em andamento é exatamente o anti-padrão descrito naquele capítulo — e numa aplicação desktop, o efeito colateral é visível e imediato: a janela trava. É o cenário mais concreto possível do motivo de nunca misturar código síncrono com assíncrono.

3. Indicando progresso durante a espera

Já que a operação é assíncrona (não trava a UI), vale mostrar ao usuário que algo está acontecendo — outra propriedade no ViewModel, ligada a um indicador visual:

C# estado de carregamento no ViewModel
private bool _carregando;
public bool Carregando
{
    get => _carregando;
    set => DefinirCampo(ref _carregando, value);
}

private async Task SalvarAsync()
{
    Carregando = true;
    try
    {
        await _repositorio.InserirAsync(new Cliente { Nome = Nome, Email = Email });
        Mensagem = "Cliente salvo com sucesso!";
    }
    catch (Exception ex)
    {
        Mensagem = $"Erro: {ex.Message}";
    }
    finally
    {
        Carregando = false;
    }
}
XAML indicador visual ligado a Carregando
<ProgressBar IsIndeterminate="True"
             Visibility="{Binding Carregando, Converter={StaticResource BoolParaVisibilidade}}" />

<Button Content="Salvar" Command="{Binding SalvarCommand}"
        IsEnabled="{Binding Carregando, Converter={StaticResource InversoBoolParaBool}}" />
Nota

WPF não converte automaticamente um bool para Visibility — um IValueConverter customizado faz essa ponte. É um detalhe específico de XAML, mas o princípio (mostrar/ocultar com base num estado do ViewModel) é o mesmo de qualquer spinner de carregamento que você já usou numa SPA JavaScript.

4. Cache local — reduzindo idas ao banco

Diferente de uma API stateless, uma aplicação desktop pode manter dados de referência (categorias de produto, lista de status) em memória durante toda a sessão, evitando reconsultar o banco a cada tela:

C# cache simples em um serviço Singleton
public class CacheReferenciaService
{
    private List<Categoria>? _categorias;

    public async Task<List<Categoria>> ObterCategoriasAsync(ICategoriaRepository repositorio)
    {
        _categorias ??= await repositorio.ListarAsync();
        return _categorias;
    }
}

5. Onde isso te leva

Com acesso a dados assíncrono e responsivo coberto, o último capítulo deste módulo (e do curso) junta tudo numa aplicação desktop completa: MVVM, navegação, data binding e as procedures do Módulo 2, do início ao fim.

📌 Resumo do capítulo

  • O mesmo Repository/procedures do Módulo 2 é chamado por um ViewModel exatamente como seria por um Controller — só muda quem aciona.
  • Nunca use .Wait()/.Result numa aplicação com UI — trava a janela inteira; sempre await.
  • Uma propriedade Carregando no ViewModel, ligada via binding, indica progresso sem bloquear a interface.
  • Dados de referência podem ser cacheados em memória durante a sessão, algo que uma API stateless não faz da mesma forma.

✏️ Praticando

  1. Adicione a propriedade Carregando ao ClienteCadastroViewModel e um ProgressBar ligado a ela.
  2. Force uma operação lenta (await Task.Delay(3000) antes da chamada real) e confirme que a janela continua respondendo (pode ser movida, redimensionada) durante a espera.
  3. Implemente um cache simples de categorias, reaproveitado entre a tela de cadastro e a tela de listagem.