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

Data Binding e Commands

Conectando XAML ao ViewModel sem uma linha de code-behind: Binding para dados, Commands para ações — o "clique de botão" do mundo MVVM.

1. Data Binding — ligando XAML a propriedades do ViewModel

{'{'}Binding NomeDaPropriedade{'}'} em XAML diz "mantenha este controle sincronizado com esta propriedade do DataContext" (o ViewModel atual da tela):

XAML ClienteCadastroView.xaml
<Window x:Class="AppClientes.ClienteCadastroView"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Novo Cliente" Height="300" Width="450">

    <StackPanel Margin="20">
        <TextBlock Text="Nome:" />
        <TextBox Text="{Binding Nome, UpdateSourceTrigger=PropertyChanged}" />

        <TextBlock Text="E-mail:" Margin="0,10,0,0" />
        <TextBox Text="{Binding Email, UpdateSourceTrigger=PropertyChanged}" />

        <TextBlock Text="{Binding Mensagem}" Foreground="Red" Margin="0,10,0,0" />

        <Button Content="Salvar" Command="{Binding SalvarCommand}" Margin="0,10,0,0" />
    </StackPanel>
</Window>
C# ClienteCadastroView.xaml.cs — code-behind mínimo
public partial class ClienteCadastroView : Window
{
    public ClienteCadastroView(ClienteCadastroViewModel viewModel)
    {
        InitializeComponent();
        DataContext = viewModel; // a única linha de "ligação" necessária
    }
}
Nota

DataContext é o objeto que todos os {'{'}Binding{'}'} daquela View "enxergam" — atribuído uma única vez. Repare que não existe mais nenhum handler de clique no code-behind: toda a lógica está no ViewModel, alcançável através de Commands.

2. ICommand — o "clique de botão" desacoplado da View

Um Command é o equivalente MVVM de uma Action de Controller (Módulo 3, capítulo 2): uma unidade de lógica que a View aciona, sem chamar um método C# diretamente.

C# RelayCommand — implementação reaproveitável de ICommand
public class RelayCommand : ICommand
{
    private readonly Action<object?> _executar;
    private readonly Func<object?, bool>? _podeExecutar;

    public RelayCommand(Action<object?> executar, Func<object?, bool>? podeExecutar = null)
    {
        _executar = executar;
        _podeExecutar = podeExecutar;
    }

    public bool CanExecute(object? parametro) => _podeExecutar?.Invoke(parametro) ?? true;
    public void Execute(object? parametro) => _executar(parametro);

    public event EventHandler? CanExecuteChanged
    {
        add    => CommandManager.RequerySuggested += value;
        remove => CommandManager.RequerySuggested -= value;
    }
}
C# usando o Command no ViewModel
public class ClienteCadastroViewModel : ViewModelBase
{
    private readonly IClienteRepository _repositorio;

    public ICommand SalvarCommand { get; }

    public ClienteCadastroViewModel(IClienteRepository repositorio)
    {
        _repositorio = repositorio;
        SalvarCommand = new RelayCommand(async _ => await SalvarAsync(), _ => PodeSalvarValida());
    }

    private bool PodeSalvarValida()
        => !string.IsNullOrWhiteSpace(Nome) && !string.IsNullOrWhiteSpace(Email);

    private async Task SalvarAsync()
    {
        try
        {
            await _repositorio.InserirAsync(new Cliente { Nome = Nome, Email = Email });
            Mensagem = "Cliente salvo com sucesso!";
        }
        catch (Exception ex)
        {
            Mensagem = $"Erro ao salvar: {ex.Message}";
        }
    }

    // Nome, Email, Mensagem como no capítulo anterior...
}
Dica

CanExecute controla automaticamente se o botão fica habilitado ou desabilitado na tela — o WPF chama essa função sozinho e ajusta a interface. Isso substitui diretamente o ModelState.IsValid do Módulo 3: em vez de validar depois do clique, o botão simplesmente não fica clicável enquanto os dados são inválidos.

3. Binding de coleções — ItemsControl e ListView

XAML lista de clientes com binding
<ListView ItemsSource="{Binding Clientes}">
    <ListView.View>
        <GridView>
            <GridViewColumn Header="Nome" DisplayMemberBinding="{Binding Nome}" />
            <GridViewColumn Header="Email" DisplayMemberBinding="{Binding Email}" />
        </GridView>
    </ListView.View>
</ListView>

Para que a lista atualize automaticamente quando itens são adicionados/removidos (não só quando uma propriedade individual muda), o ViewModel usa ObservableCollection<T> em vez de List<T> — ela já implementa a notificação de mudança de coleção, no mesmo espírito do INotifyPropertyChanged do capítulo anterior.

4. Onde isso te leva

Com data binding e Commands cobertos, o próximo capítulo trata de como navegar entre telas (abrir a lista de clientes, o cadastro, os detalhes de um pedido) mantendo o mesmo desacoplamento MVVM.

📌 Resumo do capítulo

  • {'{'}Binding Propriedade{'}'} conecta um controle XAML a uma propriedade do ViewModel, atualizando nos dois sentidos.
  • ICommand/RelayCommand é o equivalente MVVM de uma Action — CanExecute habilita/desabilita o controle automaticamente.
  • ObservableCollection<T> notifica a View quando itens são adicionados/removidos de uma lista.

✏️ Praticando

  1. Implemente RelayCommand e ligue SalvarCommand ao botão da tela de cadastro.
  2. Adicione validação via CanExecute, desabilitando o botão "Salvar" enquanto Nome ou Email estiverem vazios.
  3. Crie uma tela de listagem com ObservableCollection<Cliente> e ListView.