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):
<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>
public partial class ClienteCadastroView : Window
{
public ClienteCadastroView(ClienteCadastroViewModel viewModel)
{
InitializeComponent();
DataContext = viewModel; // a única linha de "ligação" necessária
}
}
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.
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;
}
}
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...
}
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
<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.