C# > UI Programming > WPF > MVVM Pattern

Simple WPF MVVM Example: Data Binding and Command Execution

This snippet demonstrates a basic WPF application using the MVVM (Model-View-ViewModel) pattern. It showcases data binding between the View and ViewModel, and command execution using the `ICommand` interface. It includes a simple text input and a button that updates a label with the text entered.

Project Setup

Create a new WPF application project in Visual Studio. The standard project structure is suitable for this example. You don't need to install any additional NuGet packages for this basic implementation.

Model: Data Class

The Model represents the data. In this simple example, it's just a class with a single string property. More complex applications will have more elaborate models.

public class MyModel
{
    public string Data { get; set; }
}

ViewModel: Logic and Data Presentation

The ViewModel is the core of the MVVM pattern. It holds the data to be displayed in the View and exposes commands that the View can execute. `INotifyPropertyChanged` is implemented to notify the View when properties change. `RelayCommand` is a simple implementation of `ICommand` which allows you to easily bind a method in your ViewModel to a button click or other UI event. The `UpdateCommand` is bound to the button and executes the `UpdateText` method.

using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows.Input;

public class MyViewModel : INotifyPropertyChanged
{
    private MyModel _model;
    private string _inputText;
    private string _displayText;

    public MyViewModel()
    {
        _model = new MyModel();
        UpdateCommand = new RelayCommand(UpdateText);
    }

    public string InputText
    {
        get { return _inputText; }
        set
        {
            _inputText = value;
            OnPropertyChanged();
        }
    }

    public string DisplayText
    {
        get { return _displayText; }
        set
        {
            _displayText = value;
            OnPropertyChanged();
        }
    }

    public ICommand UpdateCommand { get; private set; }

    private void UpdateText()
    {
        DisplayText = InputText;
        _model.Data = InputText; // Updating the Model, though not strictly required here
    }

    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

// Simple RelayCommand implementation
public class RelayCommand : ICommand
{
    private Action _execute;
    private Func<bool> _canExecute;

    public RelayCommand(Action execute, Func<bool> canExecute = null)
    {
        _execute = execute ?? throw new ArgumentNullException(nameof(execute));
        _canExecute = canExecute;
    }

    public bool CanExecute(object parameter)
    {
        return _canExecute == null || _canExecute();
    }

    public void Execute(object parameter)
    {
        _execute();
    }

    public event EventHandler CanExecuteChanged
    {
        add { CommandManager.RequerySuggested += value; }
        remove { CommandManager.RequerySuggested -= value; }
    }
}

View: XAML Definition

The View is responsible for displaying the data and allowing the user to interact with the application. The `TextBox`'s `Text` property is bound to the `InputText` property in the ViewModel using `Binding`. The `UpdateSourceTrigger=PropertyChanged` ensures that the ViewModel is updated as the user types. The `Button`'s `Command` property is bound to the `UpdateCommand` in the ViewModel. The `TextBlock`'s `Text` property is bound to the `DisplayText` property in the ViewModel. It is crucial that the `DataContext` of the View is set to an instance of the ViewModel (usually in the code-behind of the View).

<Window x:Class="WpfApp1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApp1"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Grid>
        <StackPanel>
            <TextBox Text="{Binding InputText, UpdateSourceTrigger=PropertyChanged}" Margin="10"/>
            <Button Content="Update Text" Command="{Binding UpdateCommand}" Margin="10"/>
            <TextBlock Text="{Binding DisplayText}" Margin="10"/>
        </StackPanel>
    </Grid>
</Window>

Code-Behind: Setting the DataContext

The code-behind sets the `DataContext` of the Window to an instance of the `MyViewModel`. This is essential for the data binding to work correctly. The View needs to know which ViewModel it's interacting with.

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        DataContext = new MyViewModel();
    }
}

Concepts Behind the Snippet

MVVM decouples the UI (View) from the application logic (ViewModel and Model). This promotes testability, maintainability, and reusability. Data binding simplifies the communication between the View and ViewModel. Commands provide a mechanism to execute actions in the ViewModel from the View without directly referencing UI elements in the ViewModel.

Real-Life Use Case

Imagine a complex form with multiple input fields, validation rules, and data transformations. MVVM allows you to separate the UI rendering from the validation and transformation logic, making the application easier to understand and maintain. For example, an employee management system could benefit from this.

Best Practices

  • Keep the View as simple as possible, focusing only on presentation.
  • The ViewModel should be testable without the View.
  • Use data binding extensively.
  • Implement `INotifyPropertyChanged` correctly to ensure the UI updates when data changes.
  • Use Dependency Injection to inject dependencies into your ViewModels, enhancing testability and flexibility.

Interview Tip

Be prepared to explain the benefits of MVVM, how data binding works, and how commands are executed. Also, be ready to discuss the role of the Model, View, and ViewModel in the pattern. Understand the concept of separation of concerns.

When to Use Them

MVVM is well-suited for complex WPF applications with a significant amount of UI interaction and data manipulation. It's particularly useful when you need to test your application logic independently of the UI. For very simple applications, the overhead of MVVM might not be justified.

Memory Footprint

MVVM can potentially increase memory usage compared to a simpler approach due to the creation of ViewModel objects and the overhead of data binding. However, the benefits of improved maintainability and testability often outweigh this consideration. Use tools like memory profilers to identify and address any memory leaks or excessive memory consumption.

Alternatives

Alternatives to MVVM include MVC (Model-View-Controller) and MVP (Model-View-Presenter). While MVC is commonly used in web development, MVP is more similar to MVVM and can be a viable alternative for WPF applications. Code Behind is an alternative for simple application. However, it has very poor testability and maintainability when the application grows.

Pros

  • Improved testability
  • Enhanced maintainability
  • Increased reusability
  • Clear separation of concerns
  • Simplified data binding

Cons

  • Increased complexity (especially for small applications)
  • Potential for increased memory usage
  • Requires more code compared to simpler approaches

FAQ

  • What is the purpose of the `INotifyPropertyChanged` interface?

    The `INotifyPropertyChanged` interface allows the ViewModel to notify the View when a property value has changed. This is essential for data binding to work correctly; when a property changes in the ViewModel, the View needs to be notified so it can update the UI.
  • Why use `RelayCommand` instead of directly handling button clicks in the code-behind?

    Using `RelayCommand` allows you to decouple the button click event from the UI. The ViewModel handles the logic associated with the button click, making the application more testable and maintainable. It also avoids directly referencing UI elements in the ViewModel, which violates the separation of concerns principle.
  • Do I always need a Model in an MVVM application?

    While the "Model" part of MVVM implies a data model, it's not always strictly necessary, especially for very simple applications. The ViewModel can directly hold the data if the data is not complex and doesn't require persistence or business logic within a separate model class. However, using a model is recommended as complexity grows to improve maintainability.