MVVM in WPF: A Study Note

In C#, code snippets (e.g., prop, ctor) auto-generate boilerplate code via double-pressing the Tab key. To view all snippets, navigate to VS → Tools → Code Snippet Manager → CSharp → VisualC#.

The following XML defines a custom propn snippet for generatnig properties with backing fields and property change notifications:

<?xml version="1.0" encoding="utf-8"?>
<CodeSnippets xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">
    <CodeSnippet Format="1.0.0">
        <Header>
            <Title>propn</Title>
            <Shortcut>propn</Shortcut>
            <Description>Code snippet for a property with a backing field and property change notification</Description>
            <Author>Microsoft Corporation</Author>
            <SnippetTypes>
                <SnippetType>Expansion</SnippetType>
            </SnippetTypes>
        </Header>
        <Snippet>
            <Declarations>
                <Literal>
                    <ID>type</ID>
                    <ToolTip>Property type</ToolTip>
                    <Default>int</Default>
                </Literal>
                <Literal>
                    <ID>propName</ID>
                    <ToolTip>Property name</ToolTip>
                    <Default>MyProperty</Default>
                </Literal>
                <Literal>
                    <ID>backingField</ID>
                    <ToolTip>Backing field for the property</ToolTip>
                    <Default>_myField</Default>
                </Literal>
            </Declarations>
            <Code Language="csharp">
        <![CDATA[private $type$ $backingField$;

public $type$ $propName$
{
    get => $backingField$;
    set 
    {
        $backingField$ = value;
        this.RaisePropertyChanged("$propName$");
    }
}
$end$]]>
            </Code>
        </Snippet>
    </CodeSnippet>
</CodeSnippets>

# 2. Folding to Definition in Visual Studio

To fold code to its definition (including `#region` blocks), use the shortcut `Ctrl+M+O`.

# 3. UI Design Essence

A picture is worth a thousand tables; a table is worth a thousand words. (A visual representation is more effective than text or tables.)

# 4. MVVM Design Pattern Explained

## 4.1 MVVM = Model-View-ViewModel

MVVM (Model-View-ViewModel) is a design pattern that separates an application into three core components:

## 4.2 Why Use MVVM?

- **Team Level**: Standardizes the development approach (consistent patterns and practices).
- **Architecture Level**: Ensures stability and decoupling (the "orange peel principle"—layers can be modified independently).
- **Code Level**: Improves readability, testability, and replaceability.

## 4.3 Components of MVVM

- **Model**: An abstraction of real-world objects (e.g., data models, business logic).
- **View**: The user interface (UI) rendered in XAML (or other UI frameworks).
- **ViewModel**: A "Model for the View"—exposes data (via properties) and actions (via commands) to the View.

### Communication Between ViewModel and View

- **Data Transfer**: ViewModel → View via properties (e.g., `DependencyProperty` for data binding).
- **Action Transfer**: View → ViewModel via commands (similar to methods or events, but more structured).

# 5. Typical MVVM Folder Structure

Common folders in an MVVM project include:
- `Models`: For data models and business logic.
- `Views`: For UI (XAML) files.
- `ViewModels`: For ViewModel classes.
- `Services`: For shared services (e.g., data access, logging).

# 6. Simple MVVM Implementation

### `NotificationObject` (ViewModel Base Class)

```csharp
/// <summary>
/// Base class for ViewModels, implementing property change notification.
/// </summary>
class NotificationObject : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    public void RaisePropertyChanged(string propertyName)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

DelegateCommand (ICommand Implementation)

class DelegateCommand : ICommand
{
    public event EventHandler CanExecuteChanged;

    public bool CanExecute(object parameter)
    {
        return CanExecuteFunc?.Invoke(parameter) ?? true;
    }

    public void Execute(object parameter)
    {
        ExecuteAction?.Invoke(parameter);
    }

    public Action<object> ExecuteAction { get; set; }
    public Func<object, bool> CanExecuteFunc { get; set; }
}

MainWindowViewModel (Concrete ViewModel)

class MainWindowViewModel : NotificationObject
{
    private double _input1;
    public double Input1
    {
        get => _input1;
        set
        {
            _input1 = value;
            RaisePropertyChanged(nameof(Input1));
        }
    }

    private double _input2;
    public double Input2
    {
        get => _input2;
        set
        {
            _input2 = value;
            RaisePropertyChanged(nameof(Input2));
        }
    }

    private double _result;
    public double Result
    {
        get => _result;
        set
        {
            _result = value;
            RaisePropertyChanged(nameof(Result));
        }
    }

    public DelegateCommand AddCommand { get; }
    public DelegateCommand SaveCommand { get; }

    private void Add(object _) => Result = Input1 + Input2;
    private void Save(object _)
    {
        var dialog = new SaveFileDialog();
        dialog.ShowDialog();
    }

    public MainWindowViewModel()
    {
        AddCommand = new DelegateCommand { ExecuteAction = Add };
        SaveCommand = new DelegateCommand { ExecuteAction = Save };
    }
}

MainWindow (XAML View)

<Window x:Class="SimpleMvvmDemo.MainWindow"
        xmlns="http://schemas.mircosoft.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:SimpleMvvmDemo"
        mc:Ignorable="d"
        Title="MVVM Demo" Height="350" Width="350">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
            <RowDefinition Height="*"/>
            <RowDefinition Height="*"/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>
        <Button Content="Save" Grid.Row="0" Command="{Binding SaveCommand}"/>
        <TextBox Grid.Row="1" Text="{Binding Input1}" Margin="10" Background="AliceBlue" 
                 TextAlignment="Center" FontSize="24"/>
        <TextBox Grid.Row="2" Text="{Binding Input2}" Margin="10" Background="AliceBlue" 
                 TextAlignment="Center" FontSize="24"/>
        <TextBox Grid.Row="3" Text="{Binding Result}" Margin="10" Background="AliceBlue" 
                 TextAlignment="Center" FontSize="24"/>
        <Button Content="Add" Grid.Row="4" Margin="10" Command="{Binding AddCommand}"/>
    </Grid>
</Window>

Code-Behind (MainWindow.xaml.cs)

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

Tags: WPF mvvm C# Code Snippet XAML

Posted on Tue, 12 May 2026 16:21:16 +0000 by BigChief