What Is MVVM and Why WPF Uses It
MVVM (Model-View-ViewModel) separates a WPF application into three layers: the Model holds domain data and business logic, the View is pure XAML that describes the UI, and the ViewModel exposes the Model's data through bindable properties and commands so the View never touches code-behind logic directly. WPF's binding engine, DependencyProperty system, and DataContext mechanism were purpose-built to make this separation practical rather than theoretical, which is why MVVM became the default architecture for non-trivial WPF apps.
Cricket analogy: Think of a scorecard app: the Model is the raw match data from the BCCI feed, the View is the animated scoreboard graphic fans see, and the ViewModel is the analyst who converts raw overs and wickets into the run-rate and required-rate numbers the graphic binds to.
Building the ViewModel Layer
A ViewModel typically implements INotifyPropertyChanged so the View is notified whenever a bound property changes, and it exposes commands (usually via ICommand implementations such as RelayCommand or DelegateCommand) instead of exposing event handlers. Because the ViewModel has no reference to any UI element, it can be unit tested in isolation — you can set a property, invoke a command, and assert on the resulting state without spinning up a Window or Application object.
Cricket analogy: A ViewModel is like a team analyst who raises a hand (PropertyChanged) every time the required run rate changes, so the broadcast graphic updates instantly — and you can test the analyst's math on paper before match day without ever running the actual broadcast.
public class PlayerViewModel : INotifyPropertyChanged
{
private string _playerName;
private int _score;
public string PlayerName
{
get => _playerName;
set { _playerName = value; OnPropertyChanged(); }
}
public int Score
{
get => _score;
set { _score = value; OnPropertyChanged(); }
}
public ICommand IncrementScoreCommand { get; }
public PlayerViewModel()
{
IncrementScoreCommand = new RelayCommand(_ => Score++, _ => !string.IsNullOrEmpty(PlayerName));
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string name = null)
=> PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}Data Binding Between View and ViewModel
The View's DataContext is set to an instance of the ViewModel (either in XAML via a DataTemplate/Resource, or in code-behind), and individual controls bind to ViewModel properties using {Binding PropertyName}. WPF's binding engine handles the plumbing: it subscribes to PropertyChanged, marshals updates onto the UI thread, and applies IValueConverter transforms when the View needs a different representation than the ViewModel exposes, such as converting a boolean IsBusy flag into a Visibility value.
Cricket analogy: Setting DataContext is like wiring a stadium's giant screen to a specific match feed — once bound, every run, wicket, or review outcome flows automatically to the screen, and a converter might turn a raw 'out' boolean into the flashing red DecisionReview graphic.
Use x:Bind instead of {Binding} in UWP/WinUI-adjacent projects for compile-time binding checks and better performance; in classic WPF, {Binding} remains the standard, but you can still catch binding errors early by watching the Output window for 'BindingExpression path error' messages during development.
Commanding with ICommand
ICommand exposes Execute and CanExecute, and WPF controls like Button automatically bind to a command's CanExecute to enable or disable themselves — no manual IsEnabled toggling required. A typical RelayCommand or DelegateCommand implementation wraps two delegates (an Action and a Func<bool>) and calls CommandManager.RequerySuggested (or an explicit RaiseCanExecuteChanged) to tell WPF when to re-evaluate whether the command is currently allowed to run.
Cricket analogy: ICommand's CanExecute is like the third umpire automatically greying out the 'review' button on the broadcast console once a team has used all its DRS reviews, without a producer manually disabling it after every ball.
<StackPanel DataContext="{Binding PlayerViewModel}">
<TextBox Text="{Binding PlayerName, UpdateSourceTrigger=PropertyChanged}" />
<TextBlock Text="{Binding Score}" FontSize="24" />
<Button Content="Add Point"
Command="{Binding IncrementScoreCommand}" />
</StackPanel>Common MVVM Pitfalls
The most frequent mistake is letting the ViewModel reach back into the View — for example, storing a Window reference to call Close() — which breaks testability and couples layers that should stay independent; use a messenger/event-aggregator or a dialog service abstraction instead. A second common issue is forgetting UpdateSourceTrigger=PropertyChanged on TextBox bindings (the default is LostFocus), leading to ViewModel state that appears stale until the control loses focus, and a third is over-engineering trivial screens with full MVVM ceremony when a simpler code-behind approach would be clearer.
Cricket analogy: Letting a ViewModel call Window.Close() is like the team analyst personally walking onto the field to signal the umpire instead of relaying the decision through the captain — it works once but breaks the chain of command the next time the broadcast changes.
Avoid CommandManager.RequerySuggested for high-frequency CanExecute checks in large view hierarchies — it re-evaluates every command in the app on nearly every UI event (focus change, keypress), which can cause noticeable lag; call RaiseCanExecuteChanged() explicitly on the specific command instead when you control the state transition.
- MVVM splits WPF apps into Model, View, and ViewModel so UI logic stays testable and decoupled from XAML.
- ViewModels implement INotifyPropertyChanged and expose ICommand properties instead of raw event handlers.
- WPF's binding engine (DataContext + {Binding}) automatically synchronizes View and ViewModel state, using IValueConverter for representation mismatches.
- ICommand's CanExecute drives automatic enabling/disabling of controls like Button without manual IsEnabled code.
- Never let a ViewModel hold a direct reference to a View element (like Window) — use services or messengers instead.
- Remember UpdateSourceTrigger=PropertyChanged on TextBox bindings; the WPF default of LostFocus surprises many newcomers.
- Don't apply full MVVM ceremony to trivial screens where simple code-behind is clearer and equally maintainable.
Practice what you learned
1. Which interface must a ViewModel implement so bound View controls update automatically when a property changes?
2. What is the default UpdateSourceTrigger for a TextBox's Text binding?
3. What does ICommand's CanExecute control in a bound Button?
4. Why is it discouraged for a ViewModel to hold a direct reference to a Window?
5. What is a common alternative to a ViewModel directly closing a dialog?
Was this page helpful?
You May Also Like
WPF Performance Tips
Practical techniques for diagnosing and fixing sluggish WPF UIs — from visual tree bloat to binding overhead and GC pressure.
WPF Interview Questions
A curated set of core WPF interview topics — binding, dependency properties, routed events, and architecture — with the reasoning interviewers expect.
WPF Quick Reference
A cheat-sheet style reference for essential XAML syntax, layout panels, data binding expressions, and commonly used WPF controls.
Related Reading
Related Study Notes in Microsoft Technologies
Browse all study notesWindows 10 / UWP Development Study Notes
.NET · 30 topics
Microsoft TechnologiesWindows Batch Scripting Study Notes
Batch · 30 topics
Microsoft TechnologiesMFC (Microsoft Foundation Classes) Study Notes
C++ · 30 topics
Microsoft TechnologiesSilverlight Study Notes
.NET · 30 topics
Microsoft TechnologiesXAML Study Notes
.NET · 30 topics
Microsoft TechnologiesMVVM Design Pattern Study Notes
.NET · 30 topics