Пошаговый мастер-класс

Биндинги в MVVM
от создания проекта до работающего кода

C#, WPF, Visual Studio 2022 / 2019

🗂️ Создадим проект с нуля
🧩 Разберём каждый класс и его библиотеку
⚡ Сделаем пример с текстом и кнопкой
Цель: понять, как работают биндинги, и начать использовать их в своих проектах.

📁 1. Создаём новый проект WPF

  1. Открываем Visual Studio.
  2. Нажимаем «Создать новый проект» (Create a new project).
  3. В поиске вводим WPF и выбираем «WPF Application (.NET)» (или .NET Core / .NET 6/7/8 — без разницы).
  4. Даём имя проекту, например, MvvmBindingDemo. Выбираем папку.
  5. Нажимаем «Создать».
✅ После создания вы увидите:
- MainWindow.xaml (окно, визуальная часть)
- MainWindow.xaml.cs (code-behind, пока пустой)
- App.xaml и App.xaml.cs (точка входа)
Пространство имён по умолчанию — MvvmBindingDemo. Все наши классы будем создавать в нём.

📂 Структура проекта в обозревателе решений

Нам нужно создать новую папку и новые классы:

Организация кода по папкам — хорошая практика, но не обязательная.

📄 2. Класс ViewModelBase (INotifyPropertyChanged)

Где создаём: папка ViewModels → добавить класс → назвать ViewModelBase.cs

Библиотека: System.ComponentModel (встроенная в .NET, не нужно устанавливать).

using System.ComponentModel;
using System.Runtime.CompilerServices;

namespace MvvmBindingDemo.ViewModels
{
public class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;

protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
От этого класса мы будем наследовать все наши ViewModel. Он уже содержит механизм уведомлений.

⌨️ 3. Класс RelayCommand (реализация ICommand)

Где создаём: папка Commands → добавить класс RelayCommand.cs.

Пространство имён: System.Windows.Input (библиотека PresentationCore уже подключена в WPF проекте).

using System;
using System.Windows.Input;

namespace MvvmBindingDemo.Commands
{
public class RelayCommand : ICommand
{
private readonly Action _execute;
private readonly Func<bool> _canExecute;

public RelayCommand(Action execute, Func<bool> canExecute = null)
{
_execute = execute;
_canExecute = canExecute;
}

public bool CanExecute(object parameter) => _canExecute == null || _canExecute();
public void Execute(object parameter) => _execute();

public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
}
}
Класс RelayCommand позволяет запускать методы из ViewModel при нажатии кнопок.

📌 4. Создаём MainViewModel

Где создаём: папка ViewModels → добавить класс MainViewModel.cs.

using MvvmBindingDemo.Commands;
using System.Windows.Input;

namespace MvvmBindingDemo.ViewModels
{
public class MainViewModel : ViewModelBase
{
private string userName = "Гость";
public string UserName
{
get => userName;
set
{
userName = value;
OnPropertyChanged();
}
}

public ICommand SayHelloCommand { get; }

public MainViewModel()
{
SayHelloCommand = new RelayCommand(() =>
{
UserName = "Привет, мир!";
});
}
}
}
Теперь у нас есть свойство UserName (с уведомлением) и команда SayHelloCommand.

🔗 5. Связываем View и ViewModel (DataContext)

Открываем MainWindow.xaml.cs (code-behind). Находим конструктор:

public MainWindow()
{
InitializeComponent();
DataContext = new MainViewModel(); // Это самая важная строка!
}

Теперь все биндинги в XAML будут искать данные в MainViewModel.

DataContext можно установить и прямо в XAML, но проще в коде для наглядности.

🖥️ 6. Пишем разметку в MainWindow.xaml

Открываем MainWindow.xaml. Внутри тега <Grid> добавляем:

<StackPanel Margin="20">
<TextBox Text="{Binding UserName, UpdateSourceTrigger=PropertyChanged}"
Margin="0,0,0,10"/>
<TextBlock Text="{Binding UserName}" FontSize="16" FontWeight="Bold"/>
<Button Content="Сказать привет" Command="{Binding SayHelloCommand}"
Margin="0,10,0,0" Padding="10"/>
</StackPanel>

Объяснение:
- TextBox привязан к UserName (двусторонне).
- TextBlock показывает то же самое.
- Button вызывает команду SayHelloCommand.

▶️ 7. Запускаем проект (F5)

Что должно произойти:

✅ Если работает — вы только что сделали первый полноценный MVVM-биндинг!

🧠 Разбор: как работает уведомление об изменении

Без вызова OnPropertyChanged() UI не узнал бы об изменении свойства. Механизм:

  1. Вы меняете свойство UserName в коде (или через команду).
  2. В сеттере вызывается OnPropertyChanged().
  3. Метод вызывает событие PropertyChanged.
  4. WPF подписан на это событие и обновляет все привязанные элементы.
set {
userName = value;
OnPropertyChanged(); // без этого UI не обновится!
}

📚 Где брать классы: таблица пространств имён

Все эти пространства имён уже доступны в WPF проекте. Ничего дополнительно устанавливать не нужно.

📋 Пример со списком: ItemsControl + ObservableCollection

Добавим в MainViewModel свойство-коллекцию:

using System.Collections.ObjectModel;

public ObservableCollection<string> Items { get; set; }
= new ObservableCollection<string>();

public ICommand AddItemCommand { get; }
// в конструкторе:
AddItemCommand = new RelayCommand(() => Items.Add($"Элемент {Items.Count+1}"));

В XAML добавляем <ListBox ItemsSource="{Binding Items}" /> и кнопку с командой AddItemCommand.

🔄 Режимы биндинга: OneWay, TwoWay, OneTime

<TextBlock Text="{Binding UserName, Mode=OneWay}" />
<TextBox Text="{Binding Age, Mode=TwoWay}" />

⏱️ UpdateSourceTrigger: когда передавать изменения из UI в ViewModel

<TextBox Text="{Binding SearchText, UpdateSourceTrigger=PropertyChanged}" />
PropertyChanged полезен для поиска, фильтрации, динамической валидации.

🔄 Конвертер: пример с видимостью

Добавляем новый класс BoolToVisibilityConverter.cs в папку, скажем, Converters.

using System;
using System.Globalization;
using System.Windows.Data;

namespace MvvmBindingDemo.Converters
{
public class BoolToVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return (bool)value ? System.Windows.Visibility.Visible : System.Windows.Visibility.Collapsed;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return value is System.Windows.Visibility visibility && visibility == System.Windows.Visibility.Visible;
}
}
}
В XAML сначала подключите пространство имён: xmlns:conv="clr-namespace:MvvmBindingDemo.Converters"

🔧 Как применить конвертер в XAML

<Window.Resources>
<conv:BoolToVisibilityConverter x:Key="BoolToVis"/>
</Window.Resources>

<Button Visibility="{Binding IsAdmin, Converter={StaticResource BoolToVis}}" .../>

Теперь если свойство IsAdmin в ViewModel равно true — кнопка видна, иначе скрыта.

📎 Передача параметров в команду

Используем CommandParameter и сигнатуру с параметром в RelayCommand.

// RelayCommand с параметром типа T
public RelayCommand(Action<object> execute) ...

// в ViewModel:
public ICommand DeleteCommand { get; }
DeleteCommand = new RelayCommand(param => Items.Remove(param.ToString()));

// XAML:
<Button Command="{Binding DeleteCommand}" CommandParameter="{Binding ElementName=listBox, Path=SelectedItem}" />

🏁 Итоги

🎯 Что делать дальше:
- Добавить валидацию через IDataErrorInfo.
- Изучить Dependency Injection с MVVM.
- Попробовать CommunityToolkit.MVVM для автоматической генерации кода.

Лекция окончена. Задавайте вопросы. Спасибо за внимание!

1 / 18