🧩 WPF: Соединение данных и элементов управления
Data Binding — самая важная тема в WPF. Без неё вы будете писать сотни строк, чтобы обновить экран. С ней — всё работает само.
✅ После лекции вы сможете:
• Объяснить, что такое привязка данных (Binding)
• Связать TextBox, TextBlock, Button с данными из кода
• Настроить DataContext
• Использовать INotifyPropertyChanged для автоматического обновления
• Писать чистый MVVM-код
📌 Для кого лекция: для начинающих. Каждая строчка кода объясняется. Никакой магии — только практика.
❓ Проблема: как обновляли экран раньше (Windows Forms)
В Windows Forms, чтобы поменять текст на кнопке или в поле, нужно было написать код вручную.
// Пример Windows Forms private void button1_Click(object sender, EventArgs e) { textBox1.Text = "Новый текст"; // вручную меняем текст label1.Text = "Значение изменилось"; // снова вручную dataGridView1.Refresh(); // и ещё раз }
😞 Минусы такого подхода:
• Много кода даже для простых действий
• Легко забыть обновить элемент
• Сложно поддерживать большие проекты
💡 WPF решил эту проблему с помощью привязки данных (Data Binding).
🔗 Что такое привязка данных (Data Binding)?
Это «мост» между интерфейсом и данными. Вы говорите: «Этот TextBox показывает свойство UserName». И WPF сам следит за изменениями.
📦 Источник (Source)
Обычная переменная или свойство в коде (C#)
🎯 Цель (Target)
Элемент XAML (TextBox, TextBlock, Button...)
<TextBox Text="{Binding UserName}" />
✨ Магия: когда свойство UserName меняется в коде, TextBox обновляется автоматически. И наоборот — когда пользователь печатает, свойство UserName меняется само.
🛠️ Минимальный рабочий пример
Создайте новый проект WPF и скопируйте код. Всё объясняется по шагам.
<!-- MainWindow.xaml --> <Window x:Class="WpfBindingDemo.MainWindow" ...> <StackPanel Margin="20"> <TextBox Text="{Binding UserName}" /> <TextBlock Text="{Binding UserName}" FontWeight="Bold" /> </StackPanel> </Window>
// MainWindow.xaml.cs public partial class MainWindow : Window { public string UserName { get; set; } = "Гость"; public MainWindow() { InitializeComponent(); DataContext = this; // 👈 ГЛАВНАЯ СТРОЧКА } }
🎯 Что здесь важно: DataContext = this — говорит WPF: «ищи свойства для привязки в этом объекте». Но у этого способа есть недостаток (нет уведомлений об изменениях).
⚠️ Ошибка: экран не обновляется после изменения свойства
private void Button_Click(object sender, RoutedEventArgs e) { UserName = "Новое имя"; // поменяли свойство // НО! TextBox и TextBlock остались старыми! }
Почему? Потому что WPF не знает, что свойство изменилось. Нужно его уведомить.
📢 Решение: внедрить интерфейс INotifyPropertyChanged.
📢 INotifyPropertyChanged — секрет автообновления
Это интерфейс, который позволяет объекту «кричать»: «Эй, UI! Моё свойство изменилось, обнови себя!»
using System.ComponentModel; using System.Runtime.CompilerServices; public class MainViewModel : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; protected void OnPropertyChanged([CallerMemberName] string prop = null) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(prop)); } private string _userName = "Гость"; public string UserName { get => _userName; set { _userName = value; OnPropertyChanged(); // ✅ вот этот вызов обновляет UI } } }
🔁 Теперь при каждом изменении UserName экран будет обновляться автоматически.
✅ Полный рабочий пример: текст + кнопка
Создайте проект, добавьте класс ViewModel и наследуйтесь.
<!-- MainWindow.xaml --> <StackPanel Margin="20"> <TextBox Text="{Binding UserName, UpdateSourceTrigger=PropertyChanged}"/> <TextBlock Text="{Binding UserName}" /> <Button Content="Сменить имя" Command="{Binding ChangeNameCommand}" /> </StackPanel>
// ViewModel public class MainViewModel : INotifyPropertyChanged { private string _userName = "Гость"; public string UserName { get => _userName; set { _userName = value; OnPropertyChanged(); } } public ICommand ChangeNameCommand { get; } public MainViewModel() { ChangeNameCommand = new RelayCommand(() => UserName = "Новое имя!"); } }
🚀 В XAML мы используем привязки (Binding), а в коде — просто меняем свойство. WPF сам обновит всё, что привязано.
⚙️ UpdateSourceTrigger — момент передачи данных из UI в источник
По умолчанию TextBox обновляет свойство только при потере фокуса. Чтобы обновлять при каждой букве — используйте PropertyChanged.
<TextBox Text="{Binding SearchText, UpdateSourceTrigger=PropertyChanged}" />
Варианты:
• LostFocus — по умолчанию, при потере фокуса
• PropertyChanged — при каждом изменении (мгновенно)
• Explicit — только при вызове UpdateSource()
💡 Для поиска, фильтрации, валидации обычно ставят PropertyChanged.
🔄 Режимы привязки (Mode)
OneWay — данные → UI (только чтение)
TwoWay — UI ↔ данные (двусторонняя)
OneTime — один раз при загрузке
OneWayToSource — UI → данные (редко)
<TextBlock Text="{Binding Score, Mode=OneWay}" /> ← только показать <TextBox Text="{Binding Age, Mode=TwoWay}" /> ← и показать, и редактировать
По умолчанию для TextBox — TwoWay, для TextBlock — OneWay.
📌 DataContext — откуда берутся данные
Это свойство любого элемента (окна, панели, кнопки). Все дочерние элементы наследуют DataContext, если не указали свой.
// Установка DataContext для всего окна public MainWindow() { InitializeComponent(); DataContext = new MainViewModel(); // 👈 все привязки ищут свойства в MainViewModel }
🔍 Как искать? WPF идёт от элемента вверх по дереву, пока не найдёт DataContext, который содержит нужное свойство.
📌 Можно установить DataContext прямо в XAML:
<Window.DataContext><local:MainViewModel/></Window.DataContext>
🎯 ElementName — привязка к другому элементу
Иногда нужно связать два элемента управления без промежуточной ViewModel.
<TextBox x:Name="InputBox" /> <TextBlock Text="{Binding Text, ElementName=InputBox}" />
✅ TextBlock будет показывать то же, что введено в TextBox. Идеально для прототипов и простых окон.
📎 RelativeSource — когда нужен родительский элемент
<Border BorderBrush="Blue" BorderThickness="1"> <TextBlock Text="{Binding RelativeSource={RelativeSource AncestorType=Border}, Path=BorderThickness}" /> </Border>
Показывает толщину рамки Border внутри самого Border. Полезно для стилей и сложных шаблонов.
💄 StringFormat — как красиво показать данные
<TextBlock Text="{Binding Price, StringFormat=Цена: {0:C}}" /> ← 1250 → "Цена: 1 250,00 ₽" <TextBlock Text="{Binding BirthDate, StringFormat=dd.MM.yyyy}" /> ← дата в нужном формате
🎨 Работает и для чисел, и для дат, и для строк. Экономит код в XAML.
🔄 IValueConverter — когда форматирование сложное
Конвертер преобразует значение из одного типа в другой (например, bool → Visibility).
public class BoolToVisibilityConverter : IValueConverter { public object Convert(object value, ...) { return (bool)value ? Visibility.Visible : Visibility.Collapsed; } public object ConvertBack(...) { ... } }
<Window.Resources> <local:BoolToVisibilityConverter x:Key="BoolToVis"/> </Window.Resources> <Button Visibility="{Binding IsAdmin, Converter={StaticResource BoolToVis}}" />
📌 Конвертеры незаменимы для видимости, цвета, включения элементов.
📋 Привязка списков: ItemsSource + ObservableCollection
Чтобы список (ListBox, ComboBox) отображал коллекцию и автоматически обновлялся при добавлении/удалении — используйте ObservableCollection.
public ObservableCollection<string> Cities { get; set; } = new ObservableCollection<string> { "Москва", "Питер", "Казань" };
<ListBox ItemsSource="{Binding Cities}" /> <Button Content="Добавить" Command="{Binding AddCityCommand}" />
🧩 ObservableCollection сама сообщает WPF об изменениях — не нужно вызывать OnPropertyChanged для всей коллекции.
⚠️ Топ‑5 ошибок при работе с привязками
1️⃣ Забыли установить DataContext → привязки не видят данные.
2️⃣ Свойство без INotifyPropertyChanged → UI не обновляется после изменения.
3️⃣ Опечатка в пути привязки → смотрите окно Output (Вывод) на ошибки привязки.
4️⃣ Mode=OneWay для TextBox → пользователь вводит, а свойство не меняется.
5️⃣ Использование List вместо ObservableCollection → добавление элементов не отображается.
🔍 Отладка привязок: установите для TextBlock Text="{Binding SomeProperty, diagnostics:PresentationTraceSources.TraceLevel=High}"
🧪 Практическое задание: «Список задач с MVVM»
Создать WPF‑приложение для управления списком дел (ToDo).
✅ Минимальные требования:
• Поле ввода (TextBox) для новой задачи
• Кнопка «Добавить» (добавляет задачу в список)
• ListBox для отображения всех задач
• Кнопка «Удалить выбранное»
• Счётчик задач (TextBlock с количеством)
• Все данные в ViewModel, INotifyPropertyChanged, ObservableCollection
🎓 Это задание закрепит всё: привязку TwoWay, команды, ObservableCollection, DataContext.
🏁 Итоги лекции
Что вы теперь знаете о соединении данных и элементов управления WPF
✅ Что такое Binding и зачем он нужен
✅ Как работает INotifyPropertyChanged
✅ Как установить DataContext
✅ Разница между OneWay, TwoWay, OneTime
✅ UpdateSourceTrigger и его значение
✅ Как работать с коллекциями (ObservableCollection)
✅ Что такое конвертеры (IValueConverter)
✅ Как отлаживать привязки
🎉 Поздравляю! Вы готовы писать профессиональные WPF-приложения без «рукожопства». Удачи в практике!