100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
.NET

MVVM in WPF — Practical Guide

A hands-on walkthrough of building WPF applications with the Model-View-ViewModel pattern, from ViewModels and commands to common pitfalls.

Practical WPFIntermediate10 min readJul 10, 2026
Analogies

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.

csharp
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.

xml
<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

Was this page helpful?

Topics covered

#NET#WPFWindowsPresentationFoundationStudyNotes#MicrosoftTechnologies#MVVMInWPFPracticalGuide#MVVM#WPF#Practical#Uses#StudyNotes#SkillVeris